SpriteGuru is a local-first, open-source sprite studio. A Python engine and a React studio turn a character description (or a sketch) into game-ready animation assets: a clean sprite sheet, per-frame PNGs, an engine atlas and an animated preview.
Everything runs on your machine. There is no account, no hosted service and no telemetry: the app talks only to the AI providers you give it keys for (see Privacy and network). Without keys, a built-in synthetic mode runs every route offline, for free and deterministically.
The core idea: every model call is shaped by a machine-readable SpriteSpec (grid, cell size,
ground line, chroma key, pose per frame), so the analyzer knows what the image should contain.
Everything after generation is a pure function of pixels plus spec: deterministic, testable and
free to re-run. The design is in docs/implementation-plan.md.
A signed and notarized SpriteGuru-<version>-arm64.dmg for Macs with Apple silicon (M1 or later)
on macOS 14 Sonoma or newer will be published on Releases.
Until then, build it yourself from a checkout with packaging/make_dmg.sh (see
Desktop build), open the DMG and drag SpriteGuru into Applications.
A self-built app is signed ad hoc, not notarized: it opens on the Mac that built it, but on another
Mac the first launch is blocked with a message that macOS can't verify it. Open it once, then go to
System Settings > Privacy & Security, click Open Anyway next to SpriteGuru and confirm (or run
xattr -dr com.apple.quarantine /Applications/SpriteGuru.app).
The command line isn't installed with the app; to script it, run from a checkout (below) or call the
bundled engine directly: /Applications/SpriteGuru.app/Contents/Resources/engine/spriteguru-engine --help.
SpriteGuru is developed and tested on macOS. The engine and studio are cross-platform Python and web code, the desktop build also targets Windows, and Linux runs from source.
| Tool | Version | Install |
|---|---|---|
| Git | any recent | https://git-scm.com |
| Python | Python 3.12 (uv installs it for you from .python-version) |
via uv |
| uv | 0.12 or newer | brew install uv, or https://docs.astral.sh/uv/getting-started/installation/ |
| Node.js and npm | Node.js 20.19+ or 22.12+ (current LTS recommended), with its bundled npm | https://nodejs.org or brew install node |
Platform notes:
- macOS: nothing else. The native window uses the system WebKit through pywebview.
- Windows: the native window uses Microsoft Edge WebView2, which ships with Windows 11 and current Windows 10. If it is missing, install the "Evergreen" runtime from Microsoft.
- Linux: pywebview needs a GTK or Qt backend for the native window, which
uv syncdoes not install. Either open the studio in your browser (uv run spriteguru studio --browser) or add the Qt backend for one run:uv run --with "pywebview[qt]" spriteguru studio. Playwright's browser also needs system libraries: install them withuv run playwright install --with-deps chromium.
git clone https://github.com/Docyrus/spriteguru.git
cd spriteguru
uv sync # Python 3.12, the engine and its dependencies, in .venv/
cd studio
npm ci # the studio's exact dependency versions from package-lock.json
npm run build # type-checks and builds the studio into src/spriteguru/studio_dist/
cd ..
uv run playwright install chromium # headless browser for vector rendering and the E2E testsThe engine serves the built studio from src/spriteguru/studio_dist/. If you skip npm run build,
the app opens a page saying "The studio UI is not built".
uv run spriteguru init Demo.sprites --style hd-cartoon --engine godot --mode synthetic
uv run spriteguru studio --project Demo.spritesThe first command creates the project folder Demo.sprites/ in the current directory, with the
synthetic simulator as its provider mode. The second opens the SpriteGuru window on the project
gallery with Demo already open: its card says Open now, and the header shows the SpriteGuru
mark, Demo and Synthetic. Click Characters in the left rail to see "No characters yet"
and the New character button. Nothing asks for a key or an account. (On Linux, add
--browser.)
The same project works from the command line, still with no keys:
cd Demo.sprites
uv run spriteguru character new knight --describe "An armoured knight with a blue tabard and a round shield."
uv run spriteguru character approve knight
uv run spriteguru gen knight attack-melee # writes animations/knight-attack-melee-E/final/
cd ..Synthetic results are placeholders drawn by a deterministic simulator: good for learning the workflow and for tests, not for your game.
Real generation uses your own accounts with these providers:
| Provider | Used for | Environment variable |
|---|---|---|
| OpenAI | turnarounds, guided sheets, repairs, in-betweens (GPT Image 2.5); the QA judge and vector motion (GPT-6 Luna, Sol) | OPENAI_API_KEY |
| fal | HD looping animation through MiniMax H3 Max video; Seedream 5.0 and FLUX.2 [max] when you pick them as image models | FAL_KEY |
| Retro Diffusion | pixel-art looping animation (optional: without it, pixel loops fall back to a guided sheet) | RD_API_KEY |
| Quiver | vector characters and idle loops (Quiver Arrow 2) | QUIVERAI_API_KEY |
Store keys in the OS keychain (macOS Keychain, Windows Credential Locker, Linux Secret Service):
uv run spriteguru keys set openai # prompts for the key; also: fal, retrodiffusion, quiver
uv run spriteguru keys status # which keys are configured, and where from; never the values
uv run spriteguru keys delete openaiOr use environment variables. For development, copy .env.example to .env in the repo root (or
your working directory) and fill in the keys you have; .env is ignored by git, and variables
already set in the environment win. SPRITEGURU_ENV_FILE=<path> points at another file. The
AI_PROVIDER_KEY_OPENAI, _FAL, _QUIVER and _RETRODIFFUSION names are accepted too.
Key values are never printed, logged, cached or returned by the API. The Settings screen in the studio manages the same keychain entries.
uv run spriteguru init MyGame.sprites --style hd-cartoon --engine godot # --mode live is the default
cd MyGame.sprites
uv run spriteguru character new knight --describe "An armoured knight with a blue tabard and a round shield."
uv run spriteguru character approve knight
uv run spriteguru gen knight attack-melee # guided sheet, best of 2, repair loop, export
uv run spriteguru gen knight walk --yes # looping HD walk via video (video calls always ask)
uv run spriteguru repair knight-attack-melee-E --frame 3 --kind pose
uv run spriteguru inbetween knight-attack-melee-E --after 3
uv run spriteguru export knight-attack-melee-E --engine phaser
uv run spriteguru ledger # spend
uv run spriteguru studio # native window (or --browser)Styles are pixel, hd-cartoon, painted and vector; engines are phaser, pixi, godot,
unity and gamemaker. Add --mode synthetic to init (or to any command) to run offline.
Spend control: every provider call is written to ledger.jsonl before it is sent. A session cap
(spend in the last 24 hours, so restarts never reset it) and a per-job cap ask before any call that
would cross them; an approval covers one more cap's worth. Video calls always ask.
Every job writes animations/<id>/jobs/<time>/ (guide canvas, mask, prompt, candidates with
provenance sidecars, per-candidate HTML reports) and animations/<id>/final/ (sheet, frames,
engine files, Aseprite JSON, GIF, report). Each export is also copied into the project's game asset
folder when one is set.
A subject has a kind. Characters are posed from joint-angle choreography on a mannequin guide.
Vehicles and machines use their own approved silhouette, moved rigidly per frame (recoil, bob,
tilt). Effects are drawn from soft shape guides (orb, tail, ring, burst, sparks); additive effects
are generated on black, matted by luminance and exported with blend: add.
| Kind | Actions |
|---|---|
| character | walk, run, idle, jump, attack-melee, cast, hurt, death, crouch, climb, push, fireball, intro, intro-salute, intro-weapon, intro-taunt, intro-leap, intro-powerup |
| vehicle | idle, move, fire, destroyed |
| machine | work, activate, break |
| effect | projectile, charge, impact, explosion, aura |
uv run spriteguru character new tank --kind vehicle --describe "An olive green battle tank with a long cannon."
uv run spriteguru character new hadouken --kind effect --blend add --describe "A blue glowing energy ball with a streaming tail."
uv run spriteguru gen tank fire
uv run spriteguru gen hadouken projectileStart from an image instead of (or as well as) a description: a sketch, concept art or an existing sprite. Any format Pillow reads is accepted, up to 20 MB.
uv run spriteguru character new knight2 --image concept.png # turnaround drawn from the image
uv run spriteguru character new knight3 --image sprite.png --use-image-as-view # the image is the side viewIn the studio, drop an image on the character form.
| Style | Looping | One-shot |
|---|---|---|
| Pixel | Retro Diffusion animation (falls back to a guided sheet without an RD key) | Guided canvas edit on GPT Image 2.5, then pixel reconstruction |
| HD cartoon, painted | MiniMax H3 Max image-to-video, cut into a loop | Guided canvas edit on GPT Image 2.5 |
| Vector | Idle: Quiver Arrow 2 micro-animation; other loops: GPT-6 Sol authored motion | GPT-6 Sol authored motion |
Model ids, fixed parameters and prices live in src/spriteguru/registry.yaml.
Every image task can use a different model: sprite sheets (guided canvas edits), turnarounds, frame repairs and in-betweens. GPT Image 2.5 (OpenAI) is the default; through fal you can also pick Seedream 5.0 Flash, Seedream 5.0 Pro, Seedream 5.0 Lite or FLUX.2 [max]. Choose in Settings, under Image models, or on the command line:
uv run spriteguru models # each task's model, maker and price
uv run spriteguru models --set guided_sheet=seedream-5.0-pro --set repair=flux-2-max
uv run spriteguru models --reset # every task back to GPT ImageA job keeps the models it started with, even if the setting changes before it resumes. Seedream and FLUX take no mask, so a repair redraws the whole canvas and keeps only the target frame. Seedream is priced per image; FLUX.2 [max] per processed megapixel, input images included (about $0.25 per candidate for a 2560x1024 sheet).
spriteguru studio starts the engine (spriteguru serve, bound to 127.0.0.1 with a per-launch
token) and opens the studio in a native window, on the project gallery unless --project opens
one. Each project is a folder, ~/Documents/SpriteGuru/<name>.sprites by default
(SPRITEGURU_LIBRARY=<dir> moves the library), and its characters, sheets, exports, cache and
ledger all live inside it. Projects opened from elsewhere show up as recent projects.
The header holds the project switcher and the active-character switcher. Every tab works on the active character: Characters (with its finished animations playing in place), Animation builder (route, cost and guide canvas before sending), Candidates, Sheet review, Frame editor, Findings (one-click remedies with costs), Export and Settings (provider keys, image models, budget).
A SpritePlay-era install carries over by itself, locally: ~/Documents/SpritePlay becomes
~/Documents/SpriteGuru (or both are listed if both exist), recent projects, project-local
preferences, browser preferences and provider keys move to their SpriteGuru names, and cloud links
and old sign-in tokens are removed from this machine. Project assets are never changed. The details
are in docs/failure-modes.md (MG1–MG10). SPRITEKIT_* environment
variables still work as fallbacks for their SPRITEGURU_* names.
uv run spriteguru ... always runs the code in src/spriteguru/. Python changes take effect on
the next command; studio changes need npm run build again, or the dev server below.
Run the engine on the fixed development port with development CORS, then Vite:
SPRITEGURU_DEV=1 uv run spriteguru serve --port 8777 --token dev --mode synthetic # add --project <p> to open one
cd studio && npm run devOpen http://127.0.0.1:5173/?token=dev. Vite serves the studio with hot reload on port 5173 and
proxies /api (HTTP and WebSocket) to the engine on 127.0.0.1:8777.
Other settings: SPRITEGURU_LIBRARY (project library folder), SPRITEGURU_PROJECT (project to
open), SPRITEGURU_VECTOR_BROWSER=auto|managed|chrome (vector renderer source),
SPRITEGURU_MODELS (model cache, default ~/.cache/spriteguru/models), SPRITEGURU_OFFLINE=1
(never download models), SPRITEGURU_MATTE_MODEL (rembg model, default birefnet-general) and
SPRITEGURU_LOG (engine log level).
End-to-end scenarios are the only tests. Each one drives the real CLI, API or studio and records
checks, and each run writes one artifact folder under e2e/artifacts/<run>/: summary.json,
index.html, the outputs, and digest.txt, a hash of the normalized results. Offline runs are
deterministic, so the digest repeats run to run.
uv run pytest e2e # offline, all scenarios, 6 in parallel (free, ~7 min on 10 cores)
uv run pytest e2e --quick # skips the evaluation matrices (~4 min)
uv run pytest e2e -n 0 -k brand # one process, one scenario, for debugging
cd studio && npm run typecheck # the studio's TypeScriptLive scenarios call real providers and spend money (about $1–2 for the whole set); they run only when asked, with keys configured:
uv run pytest e2e --live
uv run spriteguru eval gen --mode live --yes # the generation set against real providersThe subsystems tested in isolation list their failure modes first in
docs/failure-modes.md; every check in the artifact cites the IDs it covers.
The golden fixtures in tests/golden/ regenerate byte-identically with spriteguru eval fixtures.
packaging/build_app.sh # the app
packaging/make_dmg.sh # macOS: the app, then dist/SpriteGuru-<version>-<arch>.dmg and its SHA-256PyInstaller builds the engine (spriteguru-engine) and the launcher. On macOS the result is
dist/SpriteGuru.app (bundle id com.spriteguru.studio, engine in Contents/Resources/engine/), and
the script checks the bundle's name, id, version, icon and code seal (ad hoc; there is no Developer ID
signing or notarization yet); elsewhere the launcher and its engine/ folder
are in dist/SpriteGuru/, and Windows builds use brand/SpriteGuru.ico.
The app doesn't bundle Chromium: vector rendering uses Playwright's headless browser when it's
installed, else the installed Google Chrome, else it downloads the headless browser once (about
96 MB) on the first vector job.
SpriteGuru writes PNG sheets, frames and GIFs into your game's asset folder. If you keep that game in git, Git LFS keeps the repository small:
git lfs install
git lfs track "*.png" "*.gif" "*.webp"
git add .gitattributesEach project writes its own .gitignore for cache/, job folders and the machine-local
.spriteguru/ folder.
- "Executable doesn't exist" or a vector job waiting on a download: install the headless browser
with
uv run playwright install chromium(Linux:--with-deps). Offline, vector jobs fail with a plain message until it is installed. - The window doesn't open (Linux: no GTK/Qt backend; Windows: no WebView2): run
uv run spriteguru studio --browser, or see the platform notes under Requirements. - "The studio UI is not built": run
npm ci && npm run buildinstudio/. - "no keychain backend available" (headless Linux, containers): set the provider environment
variables or a
.envfile instead ofkeys set. - Port 8777 or 5173 is already in use (development only; the app itself picks a free port):
stop the other process (
lsof -i :8777), or start the engine on another port and change the proxy target instudio/vite.config.ts. - A live command says a provider key is missing:
uv run spriteguru keys statusshows what the engine sees. Keys set in a shell after the studio started need a restart. Without keys, use--mode synthetic.
src/spriteguru/
spec.py registry.yaml registry.py keys.py env.py project.py library.py cache.py ledger.py
planner.py guide.py figure.py choreo/ prompts/ generation inputs
providers/ openai fal retrodiffusion quiver synthetic hub
pipeline/ matte layout register pixel temporal video vector motion analyze browser
qa/ metrics judge score repair
export/ engine formats
eval/ fixtures golden store
jobs.py generate.py character.py service.py edits.py manual.py
api.py cli.py launcher.py report.py filelock.py
studio/ Vite + React studio (built into src/spriteguru/studio_dist/)
e2e/ E2E scenarios and the artifact writer
tests/ golden analyzer fixtures with ground truth, and scenario fixtures
packaging/ PyInstaller specs and the desktop build script
brand/ logo, marks and app icons (see brand/README.md)
notebooks/ one per pipeline module, for visual debugging
docs/ implementation plan and failure modes
SpriteGuru has no account, server, telemetry or update check. The engine listens only on 127.0.0.1, behind a per-launch token. It makes outbound connections only for:
- the AI providers you configured, with your prompts and images, when you run a live job (OpenAI, fal, Retro Diffusion, Quiver). Synthetic mode makes no provider calls;
- one-time downloads of open models and tools: the DINOv2-small identity model from Hugging Face
(skipped with
SPRITEGURU_OFFLINE=1; a built-in descriptor stands in), the rembg matte model on first use, and Playwright's headless browser for vector rendering when neither it nor Chrome is installed.
onnxruntime's own telemetry is switched off before it loads. Projects, keys, ledgers and exports stay on your disk.
Please don't describe a security issue in a public issue: open one that asks for a private contact, without details, and a maintainer will reach out.
Contributions are welcome. CONTRIBUTING.md covers the workflow: setup (this README), tests first, offline before live, and never committing keys or other people's assets.
SpriteGuru is released under the Apache License 2.0; see LICENSE and NOTICE.