Basic Cohub abilities. Most agents already have these mounted; listed here for reference.
Platform Published by the Neta team.
Official Made by the Neta team.
Verified Reviewed by the Neta team.
Community Shared by a Cohub user. Not reviewed — read SKILL.md before you use it.
sprite-sheet-prototyper
Officialneta-search/sprite-sheet-prototyper
Use it in Cohub
Paste this into your agent's chat — it fetches the skill and follows it.
Use the skill: sprite-sheet-prototyper. Run `npx -y @neta-art/neta-search-cli@latest skills use neta-search/sprite-sheet-prototyper --via site` and follow what it prints. Read its complete output, redirecting it to a temporary file first if necessary, and resolve relative paths from the skill directory it prints.
npx -y @neta-art/neta-search-cli@latest skills use neta-search/sprite-sheet-prototyper
Summary
Turn a character into a game-ready animation set: walk, run, attack, all directions.
Pick a character and an action; get the sprite sheet, individual frames, animated preview, and a ZIP in minutes.
Works from any character description or existing character; the poses come from a library of 88 verified animation references.
Delivers sheets you can drop into game engines: transparent backgrounds, eight compass directions, frame-by-frame files.
Best for characters with a clear look and distinct silhouette; dramatic moments like attacks or spell-casting work especially well.
SKILL.md
# Sprite Sheet Prototyper
Generate a game-ready character sprite sheet bundle (sheet + frames + GIF +
ZIP) from a text character description. Poses come from a motion catalog of 88
pre-verified action x direction reference images; the prompt supplies only the
character. The job ends with
the local bundle in `out/` — publishing, hosting, or engine import is the
caller's concern.
## First: which character? — check before anything else
This skill draws **one character the user chose**. Before reading the catalog,
picking a model, or generating anything, check the request:
- **It names or describes a character → go on.** Counts: a named character
(「斯内普」「大木博士」"Gojo Satoru" — a character material in this world
counts) or a written description ("a red-haired knight in silver armour").
- **It does not → stop and ask. Generate nothing until the user answers.**
Naming only the world, the series, or "a character" is **not** a character:
「给这个世界出一套行走序列帧」, "make a walk cycle for this world",
「随便来个角色」 all lack one.
- **Never pick the character yourself** — not the world's protagonist, not the
first character material, not "the most iconic one". Who gets drawn is the
user's call; a confident guess is still a guess. (XY eval: given
「给这个世界出一套行走序列帧,打包成 GIF。」 in a Harry Potter world, two
different models skipped the question and both drew 哈利·波特 — one of them
spent 17 minutes and $1.18 on a character nobody asked for.)
- **How to ask:** one short round. The character is the one question that
must be in it; if the world has character materials, offer 3–5 of them
(prefer ones with a cover image) plus a write-in, through the `questionnaire`
skill where it is available; otherwise ask in one sentence for a name or a
short description. You may fold in a couple of optional choices in the same
round (action, style, directions) — each with a sensible default — but do not
hold the run for them. Then continue with exactly the character the user gave.
## Contract
### When to pick it
The user wants game-ready 2D character animation assets, fast: a run / idle / attack cycle, a full direction set, or the same character reproduced across many actions. The pose comes from the skill's own motion catalog of 88 verified action × direction references; the prompt supplies only the character.
### Stop when the pick is wrong
Contractual deliverables — engine imports, published games, client work — go to `production-spritesheet-builder`. Publishing / hosting the bundle is the caller's job. Single-frame image editing or styling is not this skill. Do not slice a sheet this skill did not generate.
### Input
Required: **which character** — a named character (a world character material counts) or a written description of one. The world or series alone is not a character, and you never pick one on the user's behalf (see "First: which character?"). The reference image and the layout come from the motion catalog by action × direction — fetched, not authored.
Optional: action, direction, `--fps` (default 8), `--layout-file`, `--rows` / `--mirror`, `--cutout=isnet|floodfill`.
Hard rule: generation uses a reference-capable model (`gemini-3.1-flash-image-preview` or `gpt-image-2`); the neta default `krea2` declares `referenceImageInput.supported=false` and must not be used.
### Transform
confirm the character (ask if missing — never pick one) → pick action × direction → fetch that catalog reference → `neta generate image` **once**, requesting the layout's canvas, run **from the world root** → `scripts/make-game-assets.sh` slice + pack (the bundled script is the whole pipeline) → verify the five declared files in `out/` → paste the slicer's evidence.
### Deliver
The bundle in `out/`: `spritesheet.png`, `cover.png`, `frames/`, `preview.gif`, `assets.zip` — openable, shareable, engine-ready as files. With `--cutout`: `frames-alpha/`, `spritesheet-alpha.png`, `preview-alpha.gif`; with `--rows`: `gifs/<row>.gif`; with `--mirror`: `frames-mirror/`, plus `spritesheet-8dir.png` (8 rows, top→bottom N NE E SE S SW W NW; `spritesheet-8dir-alpha.png` with `--cutout`) and `directions.txt` (row → compass → row name). **An 8-direction request is delivered only when `[directions] ok: 8/8` printed** — `spritesheet.png` alone holds just the 5 generated rows. A single generated Work image is **not** the deliverable.
### Explicitly not doing
Publishing / hosting / engine import; generating per direction instead of mirroring one sheet; authoring your own build / assemble script; a README or sub-project around the job; slicing sheets it did not generate; reading generated sheets or frames into the session.
### Stop when input is missing
No character — the request names or describes none (naming only the world does not count): stop and ask, see "First: which character?". No reference-capable image model available. No motion-catalog entry for the requested action × direction (more than one match is an error, not a guess). The slicer exits non-zero (5 / 3 / 4 / 6) — a non-zero exit is not done.
## Cases
### should
「给斯内普出一套行走序列帧,打包成 GIF。」 (a character is named)
Expect: this skill, no question; one 4x2 walk sheet (one direction), sliced + packed, the five files present in `out/`. Directions are added only when asked (see theme).
### shouldNot
「把这四个方向各生成一张,再拼成 8 方向。」
Expect: do not fan out — one sheet plus mirror/rows. Generating per direction is the failure this skill must not repeat.
### missing
「给这个世界出一套行走序列帧,打包成 GIF。」 / "Make me a character animation set." (the world is named, a character is not)
Expect: stop before any generation; ask which character, offering a few of this world's characters plus a write-in. Do not pick the protagonist or any other character yourself.
### theme
「把斯内普做成跑步循环,8 个方向。」
Expect: same skill; directions were asked, so fetch the run `multi` reference from the catalog, one 5x8 sheet, mirror the other directions, slice with the directional layout plus the exact `--rows` / `--mirror` below; `[directions] ok: 8/8` and `spritesheet-8dir.png` + `directions.txt` in `out/`; the reply names the 8 directions by compass (N NE E SE S SW W NW) and hands over `spritesheet-8dir.png` as its own card.
## Delivery contract — run it, don't narrate it
An XY-plot eval of three runs found the same failure in every one: the session
**described** `spritesheet.png`, `frames/`, `preview.gif`, `cover.png`, and
`assets.zip` as finished — along the lines of "the whole asset set is packed in
`out/.../assets.zip`" and "the slice QC all passed" —
while the delivery check found **none of them on disk** and the transcript
carried no slicer call at all. The declared bundle was prose, not files. This is
the one thing this skill must not do. In all three runs the generated sheet
registered as a Work on its own — **that is not the bundle.** The delivery check
wants the five files in `out/`; a single Work image is not the deliverable.
- **Fixed order, no skipping:** `neta generate image` → `make-game-assets.sh`.
The slicer is the *next action after a successful generation*, not a later
step. A run that stops after generation has delivered nothing yet.
- **Never author your own build script.** The bundled
`scripts/make-game-assets.sh` **is** the pipeline. Runs that wrote `build.sh`
or `_assemble.sh` and "deferred" to them handed over a script instead of
assets — the same failure with extra steps. Do not hand a script over as the
product.
- **Paste raw evidence.** End the run with `make-game-assets.sh`'s `[assets]`,
`[summary]`, and `[delivery] ok` lines, quoted verbatim, plus `ls out/`. The
script exits `6` if any of the five items is missing — a non-zero exit is not
done. Never state that a frame, GIF, or zip exists unless the slicer emitted
it on this run. If you cannot paste the evidence, you did not finish.
- **One character, one action per run.** Validate the look with a single 4x2
generation, then pull the full sheet only if asked. Do not fan out to several
sheets or a full 8-direction set on your own initiative — an unbounded run was
still generating at the 15-minute cap (25 min total). 「行走序列帧」 alone is
one direction; only an explicit 「8 个方向」/"all directions" asks for the 5x8
set.
- **One image, all directions.** Generate a *single* sheet and get the other
directions from `--rows` + `--mirror` (or a directional layout) — never one
generation per direction. A run that registered 5 separate works and hit the
25-minute cap was generating per-direction. Mirroring and row slicing cost
nothing; extra generations cost minutes and money.
- **No sub-project scaffolding.** Do not build a `build.sh` / `_assemble.sh` /
`README.md` project around the job. The bundled slicer is the whole pipeline;
write only the prompt and the outputs under `out/`.
- **Generate from the world root — never `cd` into an output folder first.**
`neta generate image` registers the Work and the media artifact relative to the
current directory. Run it at the world root so they land in the canonical
`works/` and `artifacts/media/`. Generating from inside `sprites/<name>/` writes
a nested `sprites/<name>/works/` that the platform never lists — the whole bundle
exists on disk, the slice passes, and the run still shows "no product" because
its Work is buried. (Observed: a full 40-frame 8-direction bundle delivered this
way and surfaced nothing, while the other two worlds that generated at the root
showed fine.) Slicing to `./out` is local files and may run anywhere; only the
generation must be at the root.
- **Name the Work — always pass `--work-title`.** Without it the platform
titles the Work with the first words of the prompt ("Redraw the attached
image a..."). Title it `<character> · <action> sprite sheet` (add the
direction count when asked for directions), written in the user's language,
e.g. `<角色名> · 行走序列帧`. Any re-render or fix with `--target-work` must
pass the same `--work-title` again.
- **Reply in the user's language.** This file is in English; your reply is
not. The final message — the description, the evidence notes, the next
steps — follows the language of the user's request (a Chinese request gets a
Chinese reply). Raw slicer lines and file names stay verbatim.
- **Hand over with cards that point at the deliverable itself.** After the
evidence, give it through the `interaction-components` skill, each card on
its own line: (1) a `{"type": "work", "workId": "<id>"}` card for the sheet's
Work — the id is the `work.id` that `neta generate image` returned on this
run (on a `--target-work` re-render, the same id); it opens in Studio with
its name and cover; (2) a card with target
`{"type": "world-file", "path": "<out dir>/assets.zip"}`,
the path relative to the world root — the same `out/` that the slicer's
`[delivery] ok` line reported on this run — and a name/subtitle saying whose
animation it is. A markdown link or inline GIF alone is not the handover.
**8-direction run (`--mirror`): add (3) a `world-file` card for
`<out dir>/spritesheet-8dir.png`**, subtitled with the compass row order —
the Work is the raw 5-row generation (subtitle it "5 generated rows", not
"8 directions"); only the 8dir sheet shows all eight.
- **A card is a full `<interaction-json>` block, not the bare target.** The
`{"type": ...}` objects above are only the `target` field. Load the
`interaction-components` skill before the final reply and wrap each target in
its link envelope, one block per line (an eval run pasted the two bare target
objects into prose and the delivery check found no card at all):
```text
<interaction-json>
{"version": 1, "fallback": "<Work title>", "component": {"kind": "link",
"target": {"type": "work", "workId": "<work.id>"},
"presentation": {"name": "<Work title>", "subtitle": "<frames · directions>"}}}
</interaction-json>
```
The `assets.zip` card is the same envelope with the `world-file` target.
- **Every run that reached `[delivery] ok` ends with those cards.** If
EDGE-TOUCH survives re-measuring and only a re-render would clear it, do not
launch that re-render before handing over — a 5x8 re-render started at the
end of a run timed out and the session closed with no card at all. Hand over
the bundle you have, state the EDGE-TOUCH count and edge plainly as an open
issue (not "clean"), and offer the re-render as the next step.
## How to use
Happy path (short form — the detailed notes follow). Run all of this from the
world root: `neta generate image` must not be called from inside `out/` or any
sub-folder, or its Work registers where the platform cannot see it.
```bash
# 0) WHO? The request must name or describe ONE character. If it does not,
# stop here and ask (see "First: which character?") — never pick one yourself.
# A) confirm a model that accepts reference images (the neta default krea2 does not)
NETA_DEFAULT_IMAGE_MODEL=gpt-image-2 node /mods/neta/neta generate image params
# B) generate ONE sheet. Default: one action, one direction — the 4x2 rig (9:8)
NETA_DEFAULT_IMAGE_MODEL=gpt-image-2 \
node /mods/neta/neta generate image --prompt "<prompt>" --ref-file ref.png \
--work-title "<character> · <action> sprite sheet" --param size=1152x1024
# Only when the user asked for directions (「8 个方向」/"all directions"):
# the walk/run `multi` catalog entry, gemini, ONE 5x8 sheet
# NETA_DEFAULT_IMAGE_MODEL=gemini-3.1-flash-image-preview \
# node /mods/neta/neta generate image --prompt "<prompt>" --ref-file ref.png \
# --work-title "<title>" --param aspect_ratio=3:2 --param image_size=2K
# C) measure it, then slice + pack with the bundled script (never your own)
identify <generated file>
scripts/make-game-assets.sh <generated file> ./out --fps 8 --cutout=isnet
# directions run only — the 5x8 sheet needs the directional layout:
# scripts/make-game-assets.sh <generated file> ./out --fps 8 --cutout=isnet \
# --layout-file scripts/layout-directional-5x8.json \
# --rows front,front-right,side,back-right,back \
# --mirror "front-left=front-right,left=side,back-left=back-right"
# both --rows and --mirror are required for 8 directions: they make the script emit
# spritesheet-8dir.png (N NE E SE S SW W NW) + directions.txt; a missing direction exits 6
# D) verify the declared deliverable and paste the evidence
ls out/ # spritesheet.png frames/ preview.gif cover.png assets.zip must all be here
# 8-direction run: also spritesheet-8dir.png directions.txt + 8 gifs, and a
# "[directions] ok: 8/8" line — label the directions by compass in the reply
# E) reply in the user's language; hand over the sheet's Work (work.id from B)
# and out/assets.zip (world-file) — plus out/spritesheet-8dir.png (world-file)
# on an 8-direction run — as <interaction-json> link blocks via
# interaction-components — never the bare target JSON (see the Delivery contract)
```
- Three commands: pick action → generate → slice. The catalog supplies the
reference image and the layout for each action. All three are mandatory and
run in this order; stopping after `generate` is the failure above.
- The output aspect is a **model parameter, not inherited from the
reference**. Request it and the shipped layouts slice cleanly with no
re-measuring; verified three independent 5x8 generations at 0 clipped
frames each.
- Always `identify` the generated sheet, then pick the layout whose
`sourceCanvas` aspect matches. The aspect gate (exit 3) is a backstop, not
the plan.
- Trust the `[summary]` lines. `EDGE-TOUCH` is a delivery blocker; it needs
`--cutout`, otherwise the line says `SKIPPED` — which is not a pass.
- Do not `read` generated sheets or frames into the session; use `identify`,
the `[summary]` lines, and downscaled previews.
## When to use
- Create / animate / pack 2D game character assets (run / idle / attack
cycles, full direction sets)
- Reproduce an existing character across many actions cheaply
- Fast iteration where a three-command turnaround matters
## When NOT to use
- Contractual deliverables (engine imports, published games, client work) —
use `production-spritesheet-builder`
- Publishing / hosting the bundle — that is the caller's job (e.g.
`cohub public upload`), never an automatic step here
- Single-frame image editing or styling — this skill is about animation
sheets
## Prerequisites
```bash
which convert jq zip # all must exist; the neta generate CLI must be available
```
`--cutout=isnet` bootstraps a venv plus a 176MB model into
`.cache/sprite-prototyper/` on first use. Flows without `--cutout` never
touch it.
## Usage
```bash
# 0) pick a model that accepts reference images FIRST — the neta default
# (krea2) declares referenceImageInput.supported=false and will ignore them.
# Inline the env var so it scopes to THIS command only and cannot leak into
# other neta calls in the same shell (verified: the next call reverts to
# the default).
NETA_DEFAULT_IMAGE_MODEL=gemini-3.1-flash-image-preview \
node /mods/neta/neta generate image params # verify: parameters + referenceImageInput.supported=true
# 1) pick an action x direction and fetch its reference image into the
# workspace (--ref-file takes workspace-relative paths, not URLs)
jq -er '[.[] | select(.action=="run" and .direction=="front")]
| if length == 1 then .[0].ref else error("\(length) catalog entries match") end' \
assets/motion-catalog.json
curl -o ref.png "<ref url>"
# 2) generate — request the canvas your layout expects
# (measured: gemini aspect_ratio=3:2 + image_size=2K -> exactly 2528x1696;
# gpt-image-2 uses --param size=1152x1024 for 9:8)
node /mods/neta/neta generate image \
--prompt "<assembled prompt>" \
--ref-file ref.png \
--work-title "<character> · <action> sprite sheet" \
--param aspect_ratio=3:2 --param image_size=2K
identify <generated file> # from the outputs[].asset.url in the result JSON
# (inline NETA_DEFAULT_IMAGE_MODEL=... before node for the 4x2 gpt-image path)
# 3) slice and pack
scripts/make-game-assets.sh ./spritesheet.png ./out --fps 8 --cutout=isnet
```
**Pass only parameters the model declares** (`params` output is the source of
truth); an undeclared name is rejected rather than silently ignored.
Measured parameter sets (verified in the target environment):
| Model | Parameters | Notes |
|---|---|---|
| `gpt-image-2` / `gpt-image-2.5` | `size=WIDTHxHEIGHT`, `quality=auto\|low\|medium\|high` | fixed ~1.5 MP budget |
| `gemini-3.1-flash-image-preview` | `aspect_ratio=1:1\|16:9\|4:3\|3:2\|...`, `image_size=512\|1K\|2K\|4K` | 3:2 + 2K = exactly 2528x1696 |
| `krea2` (neta default) | `size` | **no reference image support — do not use for this skill** |
Requesting the right ratio is what makes the shipped layouts slice cleanly
with no re-measuring — and the ratio must match the **grid family**, not just
any non-square canvas: a 4x2 grid on a 3:2 canvas gets near-square cells that
the model fills edge-to-edge (measured: 0px left/right margins, every frame
flagged). Use `gpt-image-2` + `size=1152x1024` (9:8) for 4x2, or 1:1 with the
square layout; use `gemini` + 3:2 only for the 5x8 family. Without an aspect
parameter both reference-capable models return 1:1, and then the square
layouts apply.
Prompt templates: `references/prompts.md`. Layout families and the catalog's
`layout` field: `references/layouts.md`.
## Options
| Option | Default | Description |
|--------|---------|-------------|
| `--fps <n>` | `8` | GIF playback rate |
| `--layout-file <f>` | 4x2 9:8 | layout JSON to slice with (gates against `sourceCanvas`) |
| `--rows a,b,c` | — | row names; enables per-row `gifs/<row>.gif` |
| `--mirror "dst=src,..."` | — | flop base rows into mirrored directions (needs `--rows`) |
| `--cutout=isnet` | off | ML cutout, any background; emits `frames-alpha/`, `spritesheet-alpha.png` |
| `--cutout=floodfill` | off | fast cutout, solid backgrounds only, no model |
| `--no-aspect-gate` | off | bypass the 3% aspect check (prefer requesting the right ratio instead) |
## Output
`out/` contains `spritesheet.png cover.png frames/ preview.gif assets.zip`,
plus `frames-alpha/ preview-alpha.gif spritesheet-alpha.png` with
`--cutout`, `gifs/<row>.gif` with `--rows`, and `frames-mirror/` with
`--mirror`. With `--mirror` the script also writes `spritesheet-8dir.png`
(+ `spritesheet-8dir-alpha.png` with `--cutout`) — base and mirrored rows in
compass order N NE E SE S SW W NW — and `directions.txt`, all inside the zip;
for an 8-direction request these are the sheet you hand over and describe.
**The five plain items are the declared deliverable, and a delivery check
verifies them file-by-file.** After slicing, run `ls out/` and paste the
listing; if any of the five is absent the run is incomplete, no matter what the
prose summary claims. Produce them with `make-game-assets.sh` — never by writing
your own build script, and never by describing files you did not create.
Exit codes and the summary:
| Code / line | Meaning |
|---|---|
| `5` | layout file missing or invalid (fails before any work) |
| `3` | aspect gate: sheet vs layout `sourceCanvas` deviates > 3% |
| `4` | frame count != grid columns x rows |
| `6` | declared deliverable incomplete (one of the five bundle items absent; with `--mirror`, also the 8-direction sheet, `directions.txt`, or a row's GIF) |
| `[directions] ok: 8/8 ...` | with `--mirror`: all 8 compass directions are in `spritesheet-8dir.png` (`FAIL: no row for: ...` means a row name did not map — fix `--rows` / `--mirror`) |
| `[delivery] ok` | all five declared items are present — this is your delivery evidence |
| `[summary] frames=N dims=WxH` | sliced frame count and dimensions |
| `[summary] alpha%: ...` | per-frame subject coverage (opaque frames read 100) |
| `EDGE-TOUCH: k of N` | k frames have > 3 content pixels within 2px of a border — delivery blocker, re-measure |
| `edge-touch: SKIPPED` | no alpha channel (no `--cutout`) — **not** a pass |
## Further reading
- `references/prompts.md` — prompt templates, verified model combos, the
per-model parameter sets (`size` vs `aspect_ratio`/`image_size`), and the
measured canvas behaviour
- `references/layouts.md` — layout JSON spec, bundled layout list, square
fallbacks, 5x8 design notes
- `references/troubleshooting.md` — generation, slicing, cutout, and
environment troubleshooting
- Bundled: `assets/motion-catalog.json` (88 entries), `scripts/make-game-assets.sh`,
`scripts/build-sheet-gif.sh`, `scripts/cutout_sheet.py`, and 8 layout JSONs
(4x2 9:8 + square, 5x8 3:2 x3 + square, 2x4 landscape)