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 209 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.
The skill uses scripts as explicit pipeline commands, not as hidden imports. Each script has one job:
prepare_sprite_run.py — prepare a run from request truth: write sprite-request.json, per-state layout guides, prompts, and empty raw/ and frames/ folders.extract_sprite_row_frames.py — read generated raw/<state>.png strips, remove chroma background, extract connected sprite components, and write transparent frame PNGs plus frames/frames-manifest.json.compose_sprite_atlas.py — compose extracted frames into sprite-sheet-alpha.png and runtime manifest.json.frame_layout.preview_animation.py — build QA previews from extracted frames: contact sheets and state GIFs under qa/.compose_selected_cycle.py — record a human-selected frame subset as an explicit selected-cycle manifest plus QA GIF/contact sheet. Reads curation.json selection/transform by default; explicit --frames overrides it.compose_sprite_gif.py — export a clean transparent GIF from selected frame PNGs and optional frame order.gif_utils.py — shared transparent-GIF writer used by the GIF/QA scripts.curation.py — shared curation sidecar logic (schema + transform application) used by the compose scripts and the curation webview server. Single source of truth so they never drift.runio.py — shared safe run-dir IO: the single-writer run-dir lock (.sprite-gen.lock) and atomic temp+replace writes used by the extract/compose/export/unpack writers, so two agents (for example Claude Code and Codex in parallel) cannot silently interleave writes into one character folder.serve_curation.py — launch the standalone curation webview for one run dir (frame compare, select/reject, non-destructive rotate/scale/move). Standalone so it works from Claude Code Desktop, the Codex app, or any environment where the skill is installed.unpack_atlas_run.py — inverse of compose: rebuild a curator-ready run dir (per-frame PNGs + synthesized sprite-request.json) from a finished sprite sheet, or import a folder of separate PNGs (--pngs-dir, e.g. a furniture pack). Layout source priority: explicit --grid COLSxROWS > --manifest rectangles > auto-detect (default). Auto-detect reads the atlas alpha and clusters content blobs into a grid, so it survives a character's internal transparency on packed sheets. With --pngs-dir, a sibling meta.json (item names + iso tile/anchor) is carried into the run so the curator can label items and draw the iso ground grid.export_curated_pngs.py — export curated frames back to named PNGs (the curation transform baked in), keeping each item's original filename. Output goes inside the run dir (<run-dir>/curated/, provably writable, cross-platform); the skill never writes elsewhere in your tree. The right deliverable for an imported still set (furniture); the single-atlas compose_sprite_atlas.py is the deliverable for animation frames / runtime perf.check_visible_magenta.py — optional screenshot QA guard for visible chroma-key leakage."큐레이션(뷰) 해줘 / 이미지 후보 보여줘 / 나란히 비교 / 골라볼게" 로 진입했고 대상이 애니메이션 프레임이 아니라 임의 이미지 후보군(아이콘 시안, 로고, 생성 초안)이면, 파이프라인 없이 이 단독 경로만 쓴다. 에이전트 채팅 surface 는 이미지를 못 보여주는 경우가 많다 — 이 웹뷰가 그 표시 수단이다.
SG=${ALEX_EXTENSIONS_DIR:-$HOME/Documents/workspace/personal/alex-extensions}/sprite-gen
STAGE=$(mktemp -d /tmp/curation-XXXXXX); mkdir -p "$STAGE/pngs"
cp <후보들> "$STAGE/pngs/" # 의미 있는 이름으로: 1-hub-cube.png, 2-hook-plug.png ... (timestamp/uuid 파일명 금지)
python3 "$SG/scripts/unpack_atlas_run.py" --pngs-dir "$STAGE/pngs" --out-dir "$STAGE/run" --force
nohup python3 "$SG/scripts/serve_curation.py" --run-dir "$STAGE/run" --lang ko > "$STAGE/server.log" 2>&1 &
sleep 2
PORT=$(lsof -nP -a -p $! -iTCP -sTCP:LISTEN | awk 'END{sub(".*:","",$9); print $9}') # stdout 버퍼링 때문에 log 대신 lsof 로 포트 확보
curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:$PORT/" # 200 = positive proof, 그 후 URL 보고
--no-open + URL 전달."$STAGE/run/curation.json" 의 selected 인덱스를 파일명으로 역매핑. 비어 있으면 다시 묻는다 — 추측 진행 금지.$STAGE 정리. 후보가 1장이면 큐레이션이 아니다 — 경로만 보고하고 끝.The default user promise is deliberately simple:
A Codex user installs this skill, provides a character/base image and one or more simple actions, then receives a sprite sheet, GIF preview, and QA notes.
Do not frame the default path as game-ready humanoid locomotion. The current Codex/image-gen path is good at short readable pose changes, identity-preserving rows, chroma cleanup, atlas composition, and QA. It is not yet reliable enough to promise precise cyclic locomotion for humanoids.
Default/simple states:
idle — stable default. Use 4 frames, loop true.jump — stable default as a short non-loop action. Use 4 frames, loop false.attack — stable default as a short non-loop action. Use 4 frames, loop false.wave — simple gesture, but only stable as non-loop unless the row includes a return-to-idle frame. Use 4 frames, loop false by default; use 5 frames only when the final frame intentionally returns near frame 1.talk, blink, bounce, hurt, celebrate, magic_cast — allowed simple candidates, but still require motion QA before pass.Experimental states:
walk, run, frontwalk, 45_frontwalk, and other cyclic locomotion.For experimental states, report them as experimental in qa-notes.md unless motion QA passes. Never silently promote a weak walk/run row to the same status as simple MVP output.
When the user asks for "simple sprite animation", prefer this request shape unless they specify otherwise:
{
"states": {
"idle": { "frames": 4, "fps": 4, "loop": true, "action": "subtle breathing and one blink" },
"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": "simple jump arc: crouch, takeoff, airborne, landing" }
}
}
Add wave only as a non-loop gesture by default:
"wave": { "frames": 4, "fps": 6, "loop": false, "action": "friendly hand wave gesture; arm changes clearly while feet stay planted" }
Simple MVP pass requires:
qa/<state>.gif reads as the requested simple actionqa-notes.md records pass, best-effort, or experimental per stateKeep default simple actions short. More frames do not automatically create smoother animation in the current component-row image generation path:
4 frames is the default stable range for simple actions.5 frames is acceptable when a non-loop gesture needs a return-to-idle pose.6 frames is the conservative upper edge for simple humanoid one-shot defaults.8 frames is hatch-pet-style advanced territory, not forbidden. Use it for compact mascots, locomotion rows, or explicit experiments only when extraction/motion QA passes.9 and 12 frames are not default simple settings. In validation runs, they increased duplicate bodies, empty/sparse frames, slot collapse, and extraction failure before adding useful in-betweens.If a user asks for 9 or 12 frames, run it as an explicit experiment and report duplicate-heavy, blur/merge, or extract-fail honestly instead of treating it as a normal pass.
The row-generation pipeline has one hard ownership rule:
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
Base character images, original character sheets, and broad style references are allowed only before idle anchors are accepted. Once an idle anchor exists for the requested direction, later state rows must not attach the base character image as insurance. Re-attaching base makes the row model solve identity again and weakens the purpose of the idle-anchor workflow.
Reference ownership flow:
[USER REFS / CHARACTER SHEET]
|
v
+-----------------------------+
| 0. BASE IDLE 생성 |
| - 원본/캐릭터시트는 여기서만 사용 |
| - 비율/스타일/색/소품 고정 |
+-----------------------------+
|
v
+-----------------------------+
| 1. 방향별 IDLE ANCHOR 생성 |
| - idle-front-right |
| - idle-front-left |
| - idle-back-right |
| - idle-back-left |
+-----------------------------+
|
v
BASE CHARACTER 폐기
original refs 폐기
character sheet 폐기
이후 row 입력 금지
|
v
+-----------------------------+
| 2. BASIS ROW 생성 |
| input: |
| - target-direction idle |
| - target-state layout only |
| output: basis row |
+-----------------------------+
|
v
+-----------------------------+
| 3. PAIRED ROW 생성 |
| input: |
| - paired-direction idle |
| - paired-state layout only |
| - basis row |
| output: paired row |
+-----------------------------+
|
v
+-----------------------------+
| 4. GIF / SHEET 조립 |
| - row crop |
| - gif preview |
| - frame-index QA |
+-----------------------------+
A weak idle anchor poisons every state — proportions, style, and identity drift compound across all rows. So before any row generation you must pass an explicit gate.
Gate question, answered 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 around so the lock decision is auditable, but do not attach it again after the idle anchors have replaced it as row identity truth.
sprite-gen is released under Apache-2.0. The component-row workflow is inspired by the Apache-2.0 licensed hatch-pet skill, but this project does not include Codex pet assets, pet packages, or hatch-pet visual assets.
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.
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.
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.For simple/default states before direction-anchor mode exists, attach exactly two references:
base-source.<ext> — canonical character identityreferences/layout-guides/<state>.png — layout-only guideFor direction-anchor mode, do not attach base-source.<ext> to action rows. Attach the accepted target-direction idle anchor plus the state layout guide. For a paired row, also attach the already generated basis row as timing/scale/motion reference only.
For hatch-pet-style locomotion, attach additional references only when they are part of the row plan and record them in qa-notes.md. Useful advanced references are:
raw/running-right.png for running-left — motion rhythm onlyqa/<state>-contact.png or an approved selected-cycle contact sheet — gait readability support onlyUse prompts/<state>.txt as the prompt. Save the selected generated image as raw/<state>.png.
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>
This launches a standalone local webview (no Studio dependency — usable from Claude Code Desktop, the Codex app, or any host with the skill installed). It shows every state's frames side by side so you can compare them in parallel, toggle which frames are selected, and apply a per-frame transform (drag = move, wheel = scale, top handle = rotate, bottom-left handle = shear) when a frame's angle or position is slightly off. A live preview animates the selected frames at the state fps.
The webview UI is bilingual (English / Korean). Pass --lang en|ko to match the user's language (it is also toggleable in the app); default is en. For isometric sets imported with --pngs-dir, a sibling meta.json tile/anchor adds a ground-grid overlay for aligning furniture with the shear handle.
All edits are non-destructive: they are saved to curation.json in the run dir, and the original frames/<state>/frame-N.png files are never rewritten. The compose step bakes curation.json deterministically, so any curation decision is reversible by editing or deleting that file. The "Compose 굽기" button re-runs compose_sprite_atlas.py.
This step is optional. When there is no curation.json, every state uses all extracted frames in order with identity transform — an explicit default, not a silent fallback.
frames/ source)When only the combined sheet survives (a deployed asset whose run dir is gone), rebuild a curator-ready run dir with the inverse step before curating:
# default: auto-detect the grid by reading the atlas alpha
python3 $ALEX_EXTENSIONS_DIR/sprite-gen/scripts/unpack_atlas_run.py \
--atlas <sheet>.png --out-dir <run-dir> --force
# when a manifest carries exact rectangles (position-faithful)
python3 .../unpack_atlas_run.py --manifest <manifest>.json [--direction <dir>] --out-dir <run-dir>
# when a human states the grid, e.g. "8x9"
python3 .../unpack_atlas_run.py --atlas <sheet>.png --grid 8x9 --out-dir <run-dir>
The chosen layout source is always reported (manifest / grid-explicit / auto-detect) and stored in unpack-source.json for a later writeback. Then point serve_curation.py at the new run dir. Auto-detect is the no-instruction default; --grid and --manifest are position-faithful (they crop full cells), while auto-detect crops each blob's content bbox and centers it in the cell.
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 to the user — do not wait for them to ask. Curation is where a human accepts or fixes the result, so finishing a run means handing them the open webview, not just file paths.
Multi-agent rules for the auto-launch:
--port 0 default) and serves exactly one run dir, so several agents curating different characters can each keep a webview open with no port or state conflicts.curation.json; if one is already serving that run dir, reuse its URL instead of launching another..sprite-gen.lock): extract/compose/export/unpack fail loudly when another sprite-gen process is writing the same run dir. Treat that error as "wait or pick another run dir", not as a retry-until-success loop.--no-open and give the user the URL; on the user's own machine the default auto-opens their browser.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.
기본 simple sprite (idle/jump/attack/wave) 는 위 Workflow + 아래 QA 만으로 끝난다. 방향성·45도·locomotion 처럼 다단계 reference 가 필요한 작업은 아래 문서를 따른다 (본문에서 손실 없이 분리됨):
reference/directional-anchor-workflow.mdreference/locomotion-curation.mdprepare_sprite_run.py chooses a chroma key by sampling the base image unless the request forces one. The generated character must not use the chroma color or chroma-adjacent colors.
Choose the chroma away from the subject's dominant hue — do not blindly default to magenta. extract_sprite_row_frames.py neutralizes chroma-adjacent tint, so any character color that sits near the key gets eaten. The failure is hue-adjacency, not exact match:
When unsure, let --chroma-key auto sample the base (it scores candidates by distance from subject pixels). Only force a key when you know the subject hue is safely far from it. Verify after extraction that the dominant subject color survived — a black-where-it-should-be-colored frame means the key was adjacent to the subject.
extract_sprite_row_frames.py owns alpha cleanup for sprite rows. It removes pixels near the chroma key, removes chroma-tinted antialias fringe, neutralizes remaining key-color tint, clears fully transparent RGB, extracts connected components, and writes fresh transparent cells. This is intentionally closer to hatch-pet than to simple magick -transparent.
If component extraction cannot find the declared frame count, the row is blocked. --allow-slot-fallback exists for explicit debugging only; it must be reported as slots-explicit and is not the default path.
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.
curation.json)curation.json is an optional, non-destructive sidecar written by the curation webview (serve_curation.py) and consumed by compose_sprite_atlas.py and compose_selected_cycle.py. It records a human selection plus a per-frame affine transform; the original frame PNGs are never modified.
{
"version": 1,
"kind": "sprite-gen-curation",
"states": {
"idle": {
"selected": [0, 2, 3],
"transforms": {
"0": { "rotate": 15, "scale": 1.2, "dx": 10, "dy": -8 }
}
}
}
}
selected — 0-based frame indices in play order. Absent/empty → all extracted frames in order.transforms — keyed by 0-based frame index. rotate degrees (counter-clockwise positive, PIL convention), scale multiplier about center, dx/dy pixel offsets in the cell (+x right, +y down). Absent → identity.manifest.json.animation.rows.<state>.frames reflects the curated frame count, and manifest.json.curation_applied records whether a sidecar was used.curation.py owns this schema and the transform math so the server and the compose scripts cannot drift. If a folder exists from a previous run, create a timestamped sibling unless the user explicitly says to replace it.
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. Review motion as motion:
python3 $ALEX_EXTENSIONS_DIR/sprite-gen/scripts/preview_animation.py \
--run-dir <target>/assets/generated/sprites/<character-id>
This writes qa/<state>-contact.png (frames left-to-right) and qa/<state>.gif (played at the state fps).
The GIF is exported through the clean transparent GIF path (dedicated transparent index + disposal method 2), while the contact sheet uses a checker background for inspection.
loop: true states, the last frame must flow back into the first. A visible jump at the wrap is a fail.loop: false states such as attack, jump, hurt, or wave, judge start/middle/end readability instead of loop seam. Do not force a non-loop gesture into a loop just because it has multiple frames.qa/<state>.gif (or the contact sheet) to a fresh kuma:image-gen-style codex vision pass and ask specifically: "does this read as continuous <state> motion; is the loop seamless; is the identity stable across frames; are there anatomy or jitter problems?" Trust a second judge over a single reviewer for motion calls.If a row fails motion continuity (static bobbing, jitter, anatomy break, identity drift, or a hard loop seam), 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>
docs/architecture.md — how the scripts realize this contract: pipeline stages, the single-cell geometry model, extraction internals, curation sidecar, runtime manifest, and the hatch-pet comparison. Describes the code as-is; if it disagrees with this SKILL.md, this file wins.docs/skill-improvement-plan.md — SKILL.md / scripts 개선 후보 (quick wins · 중간 · 큰 변경) 우선순위 초안. DRAFT — 본 SKILL.md 의 SSoT 와 충돌하면 본 파일이 우선한다.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
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.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
# 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 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