by aldegad
Generate clean 2D game sprites & animation atlases — component-row pipeline: state rows, alpha cleanup, frame extraction, runtime atlases. Codex/Claude skill.
# Add to your Claude Code skills
git clone https://github.com/aldegad/sprite-genGuides for using data processing skills like sprite-gen.
Last scanned: 6/16/2026
{
"issues": [],
"status": "PASSED",
"scannedAt": "2026-06-16T09:27:00.845Z",
"npmAuditRan": true,
"pipAuditRan": true,
"promptInjectionRan": true
}sprite-gen is an open-source data processing skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by aldegad. Generate clean 2D game sprites & animation atlases — component-row pipeline: state rows, alpha cleanup, frame extraction, runtime atlases. Codex/Claude skill. It has 464 GitHub stars.
Yes. sprite-gen passed SkillsLLM's automated security scan — a dependency vulnerability audit plus prompt-injection heuristics — with no high-severity issues. You can read the full report in the Security Report section on this page.
Clone the repository with "git clone https://github.com/aldegad/sprite-gen" and add it to your Claude Code skills directory (see the Installation section above). sprite-gen ships a SKILL.md manifest, so compatible agents can discover and load it automatically.
sprite-gen is primarily written in Python. It is open-source under aldegad on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other Data Processing skills you can browse and compare side by side. Open the Data Processing category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh sprite-gen against similar tools.
No comments yet. Be the first to share your thoughts!
Top skills in this category by stars
sprite-gen builds generic game sprite atlases with a component-row pipeline:
sprite-request.json -> layout guides + prompts -> image-gen state rows
-> chroma alpha -> connected components -> transparent cells
-> sprite-sheet-alpha.png + manifest.json.frame_layout
Use only the component-row pipeline. Do not treat one-shot master sheets, fixed-grid atlas cutting, local drawing, or static fallback as a successful sprite result.
이 스킬의 모든 산출물은 아래 체크리스트를 통과해야 한다. 하나라도 어기면 그 결과물은 실패로 보고한다:
raw/<state>.png 는 중간 산출물이며, 최종 에셋은 반드시 결정론 변환 — extract_sprite_row_frames.py(크로마 제거 → 컴포넌트 분리 → 피치 검출/그리드 스냅 → kCentroid → 공유 팔레트 → 셀 배치) — 를 거친다. 같은 입력이면 항상 같은 출력이 나오는 코드 경로만 픽셀퍼펙트다.resize() 한 줄로 줄여 최종 경로에 놓는 것은 픽셀퍼펙트 변환이 아니다 — AA 가장자리 열화와 그리드 미정렬이 그대로 남는다. "이번 한 번만 빠르게" 도 금지. 파이프라인 없이 낱장만 변환할 때도 run dir 를 만들어 같은 추출 경로를 태운다.#00FF00, 녹색/청록 식물 → 마젠타 #FF00FF. 분기표 SSoT 는 image-gen SKILL.md 최상단 게이트 (상세는 docs/chroma-alpha.md).Identity ownership in the row pipeline:
identity truth = accepted idle anchor
motion truth = layout guide + paired/basis row when needed
base truth = used only to create idle anchors, then removed from row inputs
The full reference-ownership flow (base → idle anchors → base 폐기 → basis/paired rows) and the base re-attach ban live in docs/architecture.md §5.
A weak idle anchor poisons every state — proportions, style, and identity drift compound across all rows. Before any row generation, answer the gate question y/n:
Is there an image good enough to lock as the canonical base idle?
The base idle locks only when all of these hold:
If the answer is n: generate/iterate base candidates, review each against the criteria above, and re-gate. Do not run prepare_sprite_run.py until a base is locked. "Good enough for now" is not a pass — drift only grows once the rows start. When the answer is y, that exact file becomes the accepted idle anchor for its direction; keep the original generation so the lock decision is auditable, but do not attach it again after the idle anchors have replaced it as row identity truth.
Scripts are explicit pipeline commands, not hidden imports. One job each (stage detail: docs/architecture.md §2):
prepare_sprite_run.py — write sprite-request.json, per-state layout guides, prompts, and empty raw/ + frames/ from request truth.extract_sprite_row_frames.py — read raw/<state>.png strips: chroma removal → connected components → transparent frame cells + frames/frames-manifest.json.compose_sprite_atlas.py — compose sprite-sheet-alpha.png + runtime manifest.json.frame_layout.preview_animation.py — QA previews from extracted frames: contact sheets + state GIFs under qa/.compose_selected_cycle.py — record a human-selected frame subset as a selected-cycle manifest + QA GIF/contact sheet (reads curation.json by default; --frames overrides).compose_sprite_gif.py — clean transparent GIF export: single frame set, or --run-dir batch (one GIF per state from request fps + curation.json) into <run-dir>/exports/; called by the webview's Export-GIFs button and the v2 desktop app.gif_utils.py — shared transparent-GIF writer.curation.py — curation sidecar SSoT (schema + transform math) shared by the compose scripts and the webview server so they never drift.runio.py — safe run-dir IO: single-writer lock (.sprite-gen.lock) + atomic writes for the extract/compose/export/unpack writers, so parallel agents cannot interleave writes into one character folder.serve_curation.py — standalone curation webview for one run dir (works from Claude Code Desktop, the Codex app, or any host with the skill).unpack_atlas_run.py — inverse of compose: rebuild a curator-ready run dir from a finished sheet (--grid > --manifest > auto-detect) or import a PNG folder (--pngs-dir, with sibling meta.json labels/iso grid).export_curated_pngs.py — export curated frames back to named PNGs with the transform baked in, into <run-dir>/curated/; the deliverable for imported still sets.slice_sheet_cells.py — slice a multi-figure grid sheet (same character, N expressions/variants in one image) into per-cell standing cuts: v1.13 chroma alpha + centroid cell assignment + merged-figure split/in-cell re-label + neighbour-debris drop + per-cell height normalization + shared feet baseline. For dialogue cut-in portraits (立ち絵), not animation rows. Detail: docs/sheet-slicing.md.check_visible_magenta.py — optional screenshot QA guard for visible chroma-key leakage.Pass the Base Lock Gate above. Do not start step 1 until a base idle is locked (y).
Prepare the run:
python3 $ALEX_EXTENSIONS_DIR/sprite-gen/scripts/prepare_sprite_run.py \
--out-dir <target>/assets/generated/sprites/<character-id> \
--character-id <character-id> \
--base-image /absolute/path/to/base.png \
--description "<short identity note>" \
--force
For hatch-pet-style locomotion, add the cell gate explicitly: --cell-width 192 --cell-height 208.
This writes:
sprite-request.json
base-source.<ext>
references/layout-guides/<state>.png
prompts/<state>.txt
raw/
frames/
kuma:image-gen. Use prompts/<state>.txt as the prompt; save the selected image as raw/<state>.png. Reference attachment rules:base-source.<ext> (canonical identity) + references/layout-guides/<state>.png (layout only).base-source.<ext> to action rows. Attach the accepted target-direction idle anchor + the state layout guide; for a paired row also attach the basis row as timing/scale/motion reference only. Chain details: docs/directional-anchor-workflow.md.qa-notes.md: original sheet / canonical base (identity support only), a previous gait row such as raw/running-right.png (motion rhythm only), or an accepted motion-QA artifact (gait readability support only).python3 $ALEX_EXTENSIONS_DIR/sprite-gen/scripts/extract_sprite_row_frames.py \
--run-dir <target>/assets/generated/sprites/<character-id>
This removes the request chroma key, finds connected sprite components, fits each pose into a fresh transparent request-sized cell, and writes frames/<state>/frame-N.png plus frames/frames-manifest.json.
3.5. (Optional) Curate frames in the webview:
python3 $ALEX_EXTENSIONS_DIR/sprite-gen/scripts/serve_curation.py \
--run-dir <target>/assets/generated/sprites/<character-id>
Standalone local webview: side-by-side frame compare, select/reject, drag-to-reorder play sequence, non-destructive per-frame transform saved to curation.json (originals never rewritten; no sidecar = all frames in order, an explicit default). Usage detail, finished-sheet editing via unpack_atlas_run.py, and the standalone image-candidate curation path: docs/curation.md.
python3 $ALEX_EXTENSIONS_DIR/sprite-gen/scripts/compose_sprite_atlas.py \
--run-dir <target>/assets/generated/sprites/<character-id>
This writes:
sprite-sheet-alpha.png
sprite-sheet-alpha.report.json
manifest.json
manifest.json.frame_layout is the runtime SSoT. Game code must consume rectangles from the manifest and must not recover frame rectangles from alpha content at runtime.
python3 $ALEX_EXTENSIONS_DIR/sprite-gen/scripts/serve_curation.py \
--run-dir <target>/assets/generated/sprites/<character-id> &
After the atlas composes (and QA previews exist), launch the webview in the background and report the printed URL — finishing a run means handing the human the open webview, not just file paths. Multi-agent launch rules (per-launch free port, one webview per run dir, .sprite-gen.lock, --no-open for headless): docs/curation.md. Skip the auto-launch only for an explicitly unattended batch run.
Every run starts with sprite-request.json. It owns the numeric recipe used by prompts and scripts:
{
"version": 1,
"kind": "sprite-gen-request",
"engine": "component-row",
"character": { "id": "howl", "description": "same character as the base image" },
"cell": { "shape": "square", "size": 256, "safe_margin": 24 },
"chroma_key": { "name": "magenta", "hex": "#FF00FF", "rgb": [255, 0, 255] },
"states": {
"idle": { "frames": 4, "fps": 4, "loop": true, "action": "subtle breathing and blinking" },
"attack": { "frames": 4, "fps": 8, "loop": false, "action": "simple windup, strike, recovery attack pose sequence with no detached effects" },
"jump": { "frames": 4, "fps": 8, "loop": false, "action": "jump arc through body position only" },
"wave": { "frames": 4, "fps": 6, "loop": false, "action": "friendly hand wave gesture; arm changes clearly while feet stay planted" }
}
}
256 is a default variable, not a hidden constant. Change it through the request, then regenerate guides, prompts, extraction, and atlas from the same request.
Optional fit object (opt-in; absent means legacy behavior), exposed by prepare_sprite_run.py as --fit-* flags:
"fit": { "resample": "kcentroid", "align_x": "foot-centroid", "align_y": "bottom" } — pixel-art-aware downscale and jitter-free frame alignment."fit": { "pixel_perfect": true, "logical_height": 64, ... } — true pixel-perfect extraction with no non-integer resampling (per-frame pitch detection → grid snap → kCentroid → run-wide shared palette → integer NEAREST). Fully deterministic code, applied at the row-extraction stage only; the style SSoT is the attached base/anchor reference, never prompt text.docs/pixel-perfect.md.Rectangular generation cells are allowed when the target motion benefits from hatch-pet-style row proportions:
"cell": { "shape": "rect", "width": 192, "height": 208, "safe_margin_x": 18, "safe_margin_y": 16 }
The generated row uses the request cell shape. The final atlas is still consumed through manifest.json.frame_layout; runtime code must not assume square cells.
The generated row prompt must come from prompts/<state>.txt. Do not hand-write frame counts into a separate prompt. The prompt requires:
sprite-request.jsonsprite-request.jsonsprite-request.jsonIf image generation produces guide boxes, visible labels, overlapping poses, backgrounds, cropped bodies, or identity drift, regenerate the row. Do not repair bad visual generation by drawing or tiling sprites locally.
One worker owns exactly one character folder:
<target>/assets/generated/sprites/<character-id>/
sprite-request.json
base-source.<ext>
references/layout-guides/<state>.png
prompts/<state>.txt
raw/<state>.png
frames/<state>/frame-N.png
frames/frames-manifest.json
curation.json # optional, non-destructive curation sidecar
sprite-sheet-alpha.png
sprite-sheet-alpha.report.json
manifest.json
qa-notes.md
Do not let multiple workers write the same character folder. The curation.json sidecar schema (selected/order/transforms/pixel_perfect) and its folder-collision rule: docs/curation.md.
manifest.json must contain:
game_input: "sprite-sheet-alpha.png"degraded_static_fallback: falseanimation.rows.<state> with frames, fps, and loopframe_layout.rows.<state>[i] absolute atlas rectanglesRuntime must sample only the active rectangle. Rendering the whole atlas on one plane, guessing a grid, or showing a raw chroma row is a failed integration.
Static fallback is allowed only as explicit survival output when generation is blocked. It is not a sprite-gen pass and must not create sprite-sheet-alpha.png.
Automated checks (must all pass before reporting done):
frames/frames-manifest.json.ok is truesprite-sheet-alpha.report.json.ok is truescripts/check_visible_magenta.py when used in a gameStatic identity QA is not enough — a row can have the right frame count, clean alpha, and consistent identity and still animate as garbage. Build the previews and review motion as motion:
python3 $ALEX_EXTENSIONS_DIR/sprite-gen/scripts/preview_animation.py \
--run-dir <target>/assets/generated/sprites/<character-id>
The full verdict criteria (cyclic locomotion, loop seam, non-loop gestures, humanoid per-frame anatomy review, independent second opinion) live in docs/qa-motion.md. If a row fails motion continuity, regenerate that row — do not repair motion by drawing or re-timing frames locally. Record the per-state motion verdict in qa-notes.md.
Report:
sprite_gen_done=<character-id>
folder=<absolute folder path>
engine=component-row
files=sprite-request,raw,frames,atlas,manifest
qa_note=<one sentence>
Leaf docs are one link deep from this hub. Read them on these triggers:
docs/architecture.md — how the scripts realize this contract: pipeline stages, cell geometry, idle-anchor ownership flow, extraction internals, hatch-pet comparison. Read when you need code-level behavior. Describes the code as-is; if it disagrees with this SKILL.md, this file wins.docs/states-and-frames.md — choosing states and frame counts: Simple MVP scope (default vs experimental states), the Quick Path request JSON, frame-count guidance (4/5/6/8/9/12).docs/pixel-perfect.md — pixel-art targets: fit / pixel_perfect parameters, stage ownership, style/pixel-density reference rules, plain-twin curator toggle.docs/curation.md — the curation webview, the standalone image-candidate curation path (icons/logos/drafts — not sprites), finished-sheet editing (unpack_atlas_run.py), multi-agent launch rules, the curation.json schema.docs/chroma-alpha.md — chroma key selection branches, --chroma-key auto scoring, extraction-side alpha cleanup, slot-fallback policy.docs/sheet-slicing.md — multi-figure variant sheets → per-cell standing cuts (slice-sheet): geometry rules, field failure modes, sheet prompting. Read when cutting tachi-e/portrait sheets.docs/qa-motion.md — Motion Continuity verdict criteria in full.docs/directional-anchor-workflow.md — directional / 45° anchor chains, hatch-pet locomotion patterns, advanced gates.docs/locomotion-curation.md — motion phase guide experiments, manual selected cycles, clean GIF export.English · 한국어 · 日本語 · 简体中文 · Español · Français
Ask an image model for a "sprite sheet" and you know what you get: a character whose face changes every frame, a background that won't key out, poses that overlap and drift off-grid, and a PNG your game engine can't actually consume. Cute demo, useless asset.
sprite-gen is a Codex/Claude skill that closes that gap. Give it one base image and a list of actions — it drives the generation row by row, locks the character's identity, strips the chroma background to real alpha, extracts each pose as a clean transparent frame, and bakes a runtime atlas with a machine-readable manifest.json.frame_layout. Every sprite above was made this way.
And for the last 10% that generation never gets right, there's a curation webview: compare frames side by side, reject the broken ones, nudge rotation/scale/position non-destructively, watch the loop live — then bake. The pipeline does the labor; you keep the taste.
sprite-request.json → layout guides + prompts → image-gen state rows
→ chroma alpha → connected components → transparent frames
→ sprite-sheet-alpha.png + manifest.json.frame_layout
flowchart LR
REQ["sprite-request.json<br/>(numeric SSoT)"] --> GUIDES["layout guides<br/>+ prompts"]
GUIDES --> GEN["image-gen<br/>state row strips"]
GEN --> EXTRACT["chroma alpha →<br/>connected components"]
EXTRACT --> FRAMES["transparent frames"]
FRAMES --> ATLAS["sprite-sheet-alpha.png<br/>+ manifest.json.frame_layout"]
FRAMES -. "curation webview (optional)" .-> ATLAS
Full architecture:
docs/architecture.md
sprite-sheet-alpha.png) — real alpha, no leftover chroma fringe, verified against white backgrounds.manifest.json.frame_layout) — absolute frame rectangles, per-state fps and loop flags. Your engine samples rectangles; it never guesses a grid.The extractor keeps chroma cleanup deterministic: soft-alpha unmix preserves antialiased hair strands and thin outlines instead of peeling them away before coverage can be solved.
The close-up crops below show the edge detail behind the full-body comparisons.

![]()
Generation gets you 90%. The webview is where a human takes it to shipped — standalone, no Studio or framework dependency, runs anywhere the skill is installed (Claude Code Desktop, the Codex app, a plain terminal).

curation.json sidecar — source PNGs are never rewritten, and the compose step bakes the result deterministically. Preview and bake share one affine matrix, so what you align is what you get.unpack_atlas_run.py --pngs-dir and use it as a general pick-the-winner view.For isometric sets, the webview overlays the floor grid (from meta.json tile/anchor) so you can snap furniture to the diamond axes with the shear handle.

The webview ships with English and Korean. Pass --lang en|ko when launching, or use the in-app toggle:
python3 scripts/serve_curation.py --run-dir <run-dir> --lang en # or ko
sprite-gen supports CPython 3.10+. CI runs the minimum supported version (3.10) and the latest covered version (3.14) on GitHub-hosted runners.
The quickstart requires a Python install with working venv/ensurepip. If python3 -m venv fails before package installation in a local distribution, use a standard CPython build for any supported version and rerun the same commands.
# 0. install dependencies (Pillow) into a fresh virtualenv
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
# 1. prepare a run from a base image
python3 scripts/prepare_sprite_run.py --out-dir <run-dir> --character-id <id> --base-image base.png
# 2. generate one row image per state with image-gen, save as raw/<state>.png
# 3. extract frames
python3 scripts/extract_sprite_row_frames.py --run-dir <run-dir>
# 4. (optional) curate frames in the webview
python3 scripts/serve_curation.py --run-dir <run-dir>
# 5. bake the runtime atlas
python3 scripts/compose_sprite_atlas.py --run-dir <run-dir>
When only the combined sheet survives, rebuild a curator-ready run dir, then curate and export:
# rebuild frames: explicit --grid, --manifest rectangles, or alpha auto-detect (default)
python3 scripts/unpack_atlas_run.py --atlas sheet.png # auto-detect
python3 scripts/unpack_atlas_run.py --manifest manifest.json # exact rectangles
python3 scripts/unpack_atlas_run.py --pngs-dir furniture/ # import a loose PNG set
# after curating, bake corrections back to named PNGs
python3 scripts/export_curated_pngs.py --run-dir <run-dir>
Output defaults to a findable <source>-curator folder next to the input.
The full agent-facing workflow and contracts live in SKILL.md.
From Codex skill installer workflows, install this repository as a root skill:
python3 ~/.codex/skills/.system/skill-installer/scripts/install-skill-from-github.py \
--repo aldegad/sprite-gen --path .
The raw row images (step 2 of the quickstart) are generated by the separate image-gen skill (declared as kuma:image-gen in SKILL.md depends_on). Install it the same way:
python3 ~/.codex/skills/.system/skill-installer/scripts/install-skill-from-github.py \
--repo aldegad/image-gen --path .
The component-row workflow is inspired by the Apache-2.0 licensed hatch-pet skill, but targets generic game sprite atlases and includes no pet packages or pet visual assets.
Apache-2.0