Home
Softono
sprite-sheet-helper

sprite-sheet-helper

Open source MIT TypeScript
12
Stars
1
Forks
0
Issues
0
Watchers
1 week
Last Commit

About sprite-sheet-helper

Create sprite sheets, gifs and assets from 3D models

Platforms

Web Self-hosted

Languages

TypeScript

🧱 Sprite Sheet Helper

Generate sprite sheets and export renders from 3D scenes β€” fast and simple.

Built with Vite + React + Tauri for a lightweight, native desktop experience.


https://github.com/user-attachments/assets/cc9d06c4-2281-4131-a87c-5a95b9defe4e

πŸ“¦ Download

Prebuilt desktop binaries are available on the Releases page.

Platform File
macOS .dmg
Windows .msi or .exe
Linux .AppImage or .deb

Download the appropriate file for your OS and run it directly β€” no setup required.


Documentation

✨ Features

Supported model formats

Format Notes
GLB Recommended β€” fully self-contained
GLTF External .bin and textures must be co-located
FBX Binary FBX recommended
OBJ Geometry only; .mtl materials not loaded

Scene setup

  • Lights β€” Ambient, Directional, Point, Spot with adjustable intensity and color
  • Camera β€” Orbit controls with preset angles: Top-down, Isometric (45Β° / 225Β°), ΒΎ RPG (55Β°), and more
  • Transform controls β€” Reposition, rotate, and scale objects in the scene
  • Post-processing effects β€” Pixelation, Bloom, Outline, Glitch, Depth of Field, ASCII, Dither, Palette, SSAO, and 20+ more

Animations

  • Loads animation clips embedded in the model
  • Timeline editor with play/pause, frame stepping, and per-clip trim/speed/loop settings
  • Per-frame capture synced to animation playback

Camera animation capture

Record animation clips directly from a webcam or a photo β€” no 3D animation software needed.

  • Live recording β€” pose detection runs in-browser via MediaPipe; record at up to 30 FPS
  • Photo mode β€” upload a single image to capture a static pose
  • Review step β€” scrub through every captured frame before saving:
    • Trim start/end and delete individual frames
    • Global corrections: tilt (X), turn (Y), lean (Z), mirror L↔R, flip 180Β°
    • Per-bone overrides: X/Y/Z euler sliders per bone per frame, with "apply to all frames" option
  • Captured clips are saved to the model and available for export alongside embedded animations

Export formats

Format Output
spritesheet PNG + JSON metadata
zip Raw frame PNGs in a ZIP
gif Animated GIF
bevy Rust structs + Cargo.toml snippet
godot GDScript + resource file
unity C# SpriteSheetAnimator class
phaser Atlas JSON for Phaser 3
pygame Python module
raylib C header
love2d-lua Lua module for LΓ–VE 2D
love2d-anim8 anim8 library format
turbo Turbo engine format

Project files

Save and load .sshProj files to preserve the full scene state β€” objects, lights, camera, animations, materials, spritesheet postprocess settings, and undo history included.

Spritesheet postprocess

The Export Workbench can apply 2D effects after capture and before atlas packing:

  • Outer Outline, including crisp pixel outlines
  • Drop Shadow and Glow
  • Color Adjust
  • Before/after preview with a draggable divider

Normal-map atlases stay clean: color frames are processed, while matching normal frames are only padded when needed so atlas rects stay aligned.


πŸ–₯ CLI

The CLI drives the app headlessly: it starts a local preview server, injects the model into a Puppeteer browser, captures frames, and writes the exported files to disk. No GUI required.

Before running

Build the app with the CLI bridge before running:

npm run build:cli

Usage

npm run cli -- <input> [options]
# or after npm link / global install:
sprite-sheet-helper <input> [options]

Options

Option Default Description
--format spritesheet Export format (see table above)
--frames 8 Number of frames to capture per animation sequence
--fps 10 Playback frame rate
--width 64 Frame width in pixels
--height 64 Frame height in pixels
--output ./out Output directory
--port 4174 Port for the local preview server
--workflow β€” Workflow preset ID (see table below)
--cameraDistance 5 Camera distance from the model (used with --workflow)
--cameraAngle / --phi β€” Workflow camera elevation override in degrees
--directionRotationOffset β€” Rotate every workflow direction by this many degrees
--target β€” Camera target as x,y,z
--directionOverride β€” Per-direction camera override, e.g. N:phi=45,theta=0,distance=3
--normalMap false Capture and export a matching normal atlas
--skipStepLabel β€” Skip one workflow row label (repeatable).
--skipStepLabels β€” Comma-separated workflow row labels to skip.
--forceAnimationsInPlace false Force workflow animation playback to stay at the same root location.
--captureNormalMaps false Override normal-map capture for workflow runs.
--atlasLayout rows Atlas layout: rows or packed
--atlasPadding 0 Empty pixels around each frame slot
--atlasBleed 0 Edge-pixel extrusion into padding
--atlasScale 1 Scale atlas frame dimensions
--maxAtlasSize 2048 Maximum atlas page width and height
--multiPage false Allow generic spritesheet page splitting
--config β€” Run one or more JSON-configured jobs
--job β€” Run a single job from a config file
--dryRun false Print resolved jobs without launching the browser
--json false Emit a machine-readable summary
--writeSummary β€” Write the JSON summary to a file
--failOnWarnings false Exit with an error when warnings exist
--quiet false Suppress progress logs
--debug false Show browser console output
--headful false Launch Chromium with a visible window
--timeout β€” Per-job timeout in milliseconds
--exportTimeout 60000 Export download timeout in milliseconds
--workflowTimeout β€” Workflow completion timeout in milliseconds

Format aliases: bevy-rust β†’ bevy, love2d β†’ love2d-lua

Quick discovery commands:

sprite-sheet-helper --help
sprite-sheet-helper --list-formats
sprite-sheet-helper --list-workflows

Examples

# 8-frame spritesheet at 64Γ—64 (defaults)
sprite-sheet-helper character.glb

# LΓ–VE 2D export, 16 frames at 128Γ—128
sprite-sheet-helper character.glb --format love2d --frames 16 --width 128 --height 128

# Animated GIF
sprite-sheet-helper character.fbx --format gif --frames 12 --fps 12 --output ./exports

# Packed generic atlas with page splitting
sprite-sheet-helper character.glb --format spritesheet --atlasLayout packed --multiPage true --maxAtlasSize 1024

# Workflow camera override with JSON output
sprite-sheet-helper character.glb --workflow topdown-4dir --cameraAngle 45 --target 0,0.8,0 --json

Batch config

{
  "defaults": { "format": "spritesheet", "frames": 4 },
  "jobs": [
    {
      "id": "hero-topdown",
      "input": "assets/hero.glb",
      "output": "dist/hero",
      "workflow": "topdown-4dir"
    }
  ]
}

Run all jobs, one job, or inspect the resolved settings:

sprite-sheet-helper --config sprites.json
sprite-sheet-helper --config sprites.json --job hero-topdown
sprite-sheet-helper --config sprites.json --dryRun

Docker

Use the GHCR image when you want local or CI automation without installing Node or Chromium:

docker run --rm \
  -v "$PWD:/work" \
  ghcr.io/kyonru/sprite-sheet-helper:v0 \
  /work/assets/hero.glb \
  --workflow topdown-4dir \
  --format godot \
  --output /work/dist/sprites

For batch pipelines:

docker run --rm \
  -v "$PWD:/work" \
  ghcr.io/kyonru/sprite-sheet-helper:v0 \
  --config /work/sprites.json \
  --writeSummary /work/dist/sprites-summary.json

GitHub Action

- name: Generate sprites
  uses: Kyonru/sprite-sheet-helper/action@main
  with:
    input: assets/hero.glb
    output: dist/sprites
    workflow: topdown-4dir
    format: godot

The action also supports config-driven batches:

- name: Generate sprite batch
  uses: Kyonru/sprite-sheet-helper/action@main
  with:
    config: sprites.json
    fail-on-warnings: "true"

On release tags like v0.4.0, CI publishes Docker image tags v0.4.0, v0.4, v0, and latest. Branch builds do not update published image tags.

CI example project

See example/ for a standalone GitHub repository template that discovers every model in a models/ folder, runs the Docker Action on push to main, and uploads generated sprite artifacts.

Example output β€” --format love2d

out/
  spritesheet.png
  spritesheet.json
  spritesheet.lua
  main.lua

πŸ”„ Workflows (CLI)

Workflows automate multi-angle sprite sheet generation. Instead of capturing a single sequence, a workflow iterates over every animation embedded in the model and every camera direction defined by the preset β€” producing one labeled sequence per combination.

Each row in the output is named {animationName}_{direction}, for example walk_N, idle_SE, run_Left.

Workflow presets

ID Label Directions Camera elevation
topdown-8dir Top Down 8-directional N, NE, E, SE, S, SW, W, NW ~overhead (phi=1Β°)
topdown-4dir Top Down 4-directional N, E, S, W ~overhead (phi=1Β°)
isometric Isometric SE, NE, NW, SW 45Β° elevation
platformer Platformer / Side View Right, Left Horizon (phi=90Β°)

Workflow usage

sprite-sheet-helper character.glb --workflow <id> [options]

Workflow-specific options

Option Default Description
--workflow β€” Workflow preset ID (required for workflow mode)
--frames 8 Frames captured per animation Γ— direction combination
--fps 10 Playback frame rate for each capture
--width 64 Frame width in pixels
--height 64 Frame height in pixels
--cameraDistance 5 Distance of the camera from the model origin
--cameraAngle / --phi preset Camera elevation override in degrees
--directionRotationOffset 0 Rotate preset directions
--target 0,0,0 Camera target as x,y,z
--skipStepLabels β€” Comma-separated workflow step labels to skip.
--forceAnimationsInPlace false Keep animations in place while rendering workflow steps.
--captureNormalMaps false Override normal-map capture for the workflow.
--format spritesheet Export format applied to the full multi-sequence output
--output ./out Output directory

Workflow examples

# Top-down 8-directional sheet β€” all animations, all 8 angles
sprite-sheet-helper character.glb --workflow topdown-8dir

# Isometric at 128Γ—128, exported as a Godot resource
sprite-sheet-helper character.glb --workflow isometric --width 128 --height 128 --format godot

# Platformer workflow with closer camera and more frames
sprite-sheet-helper character.glb --workflow platformer --cameraDistance 3 --frames 16 --fps 12

# Top-down 4-dir, exported as a Bevy plugin
sprite-sheet-helper character.glb --workflow topdown-4dir --format bevy --output ./bevy-assets

# Reframe and rotate a workflow before capture
sprite-sheet-helper character.glb --workflow isometric --cameraAngle 40 --directionRotationOffset 15 --target 0,0.8,0

Example output β€” topdown-4dir with a model that has idle and walk clips

out/
  spritesheet.png       ← atlas image with all 8 sequences (2 anims Γ— 4 dirs)
  spritesheet.json      ← metadata: frame size, quads, animation names

The JSON metadata labels each animation strip:

{
  "animations": [
    { "name": "idle_N", "fps": 10, "frameWidth": 64, "frameHeight": 64, "quads": [...] },
    { "name": "idle_E", "fps": 10, ... },
    { "name": "idle_S", "fps": 10, ... },
    { "name": "idle_W", "fps": 10, ... },
    { "name": "walk_N", "fps": 10, ... },
    { "name": "walk_E", "fps": 10, ... },
    { "name": "walk_S", "fps": 10, ... },
    { "name": "walk_W", "fps": 10, ... }
  ]
}

πŸš€ Development Setup

Prerequisites

1. Clone the Repository

git clone https://github.com/Kyonru/sprite-sheet-helper.git
cd sprite-sheet-helper

2. Install Dependencies

npm install

3. Run in Development Mode

npm run tauri dev

This starts Vite and launches the Tauri desktop app with hot reload.


πŸ›  Build

Debug build

npm run tauri build -- --debug

Production build

npm run tauri build

The compiled application will be output to src-tauri/target/.


πŸ“œ Scripts

Command Description
npm install Install all dependencies
npm run tauri dev Run the app in development mode
npm run tauri build Build the production desktop app
npm run tauri build -- --debug Build a debug desktop app
npm run build Build the web frontend only

πŸ— Architecture

Sprite Sheet Helper is pwa + web-first. The app runs as a standard web app by default and is also packaged as a native desktop app via Tauri.

Platform-specific Code

When a feature requires different implementations between web and native (Tauri), the following file naming convention is used:

File Used when
feature.web.ts Default β€” runs in the browser / web build
feature.tauri.ts Native override β€” runs in the Tauri desktop build

At build time, a Vite plugin (WebTauriSwapPlugin) automatically swaps .web.* imports for their .tauri.* counterpart when building for desktop. If no .tauri.* file exists, the .web.* version is used as the fallback.

Example:

src/components/reload-prompt/
β”œβ”€β”€ prompt.web.ts     ← used in web builds (PWA service worker)
└── prompt.tauri.ts   ← used in Tauri builds (no-op or native equivalent)

When adding a feature that behaves differently on each platform:

  1. Implement the web version in feature.web.ts
  2. Implement the native version in feature.tauri.ts
  3. Import using the .web suffix β€” the build system handles the rest:
import useMyFeature from "./feature.web";

ℹ️ Never import .tauri.* files directly. Always import the .web.* version and let the plugin swap it at build time.


🀝 Contributing

Contributions are welcome! Here's how to get started:

# 1. Fork and clone the repo, then:
git checkout -b feature/your-feature-name

# 2. Install dependencies
npm install

# 3. Make your changes, then commit
git commit -m "feat: describe your change"

# 4. Push and open a Pull Request
git push origin feature/your-feature-name

Please keep commits clear and scoped. Bug fixes, performance improvements, and new features are all appreciated.