# Character art: one animation, any outfit Player sprites are paper dolls. PixelLab is called a fixed number of times to build a **layer library**; every look a player can create is composed from it on the CPU with no further API calls. ```mermaid flowchart LR A[create-character-with-4-directions
bald, white tee, grey shorts, barefoot] --> B[walking-4-frames template
south, east, north] B --> C[15 base frames
3 dirs x stand + 4 walk] C --> D[edit-animation-v2 per item
all 15 frames in one call,
item drawn in pure green] C --> E[split base: skin / tee / shorts
shade layers + fixed pixels] D --> F[extract green hue -> shade layer
outline + details -> fixed pixels] E --> G[assets/characters/<body>/] F --> G G --> H[client composes any look:
skin tone, item per slot, color per slot] ``` ## Why it works - The base walk is generated once per body. `edit-animation-v2` edits all 15 frames in one request and keeps the motion, so an item's frames line up pixel-for-pixel with the base frames. No per-outfit animation calls. - Items are drawn in a stand-in color (bright pure green) that no base pixel uses. The extractor (`tools/character_art.py build`) keeps the green pixels as a **shade layer**: each pixel stores a shade index 1..4 (luminance relative to the frame's median, so frames the model rendered a bit brighter or darker still band the same). The dark outline and small details right next to the green become **fixed pixels**. - The base body is split the same way: skin, tee and shorts become shade layers, everything else (outline, eyes, mouth) stays fixed. Skin tone is therefore just a color, like a shirt. - Tops are clipped two rows below the shorts' waistline (the edit model sometimes turns a sweater into a robe). - West is east mirrored at runtime, so only three directions are stored. ## Cost | Step | Calls | Generations | | --- | --- | --- | | Base body (x2) | `create-character-with-4-directions` | ~1 each | | Walk (x2) | `characters/animations` template mode, 3 directions | ~0 (template) | | Item (x18 per body) | `edit-animation-v2`, 15 frames | 20 each | | Professor Henry | character + walk + `create-image-pixen` portrait | ~2 | 18 items x 2 bodies = 720 generations for every combination of 5 hair styles, 3 hats, 4 tops (+ the base tee), 3 bottoms (+ the base shorts) and 3 shoes, in any color and any of 6 skin tones. A new item is one more call per body. ## Files ``` art_src/characters// raw PixelLab output (rotations, walk4/, items//frame_N.png) assets/characters/manifest.json slots, z order, skin tones, palette, items, bodies, NPCs assets/characters//body.png fixed pixels of the base body (sheet) assets/characters//skin_shade.png, top_tee_shade.png, bottom_shorts_shade.png assets/characters//.png + _shade.png assets/characters/henry/henry.png + portrait.png ``` Sheets are 5 columns (standing, walk 0..3) x 3 rows (south, east, north) of 64 px cells. Shade layers store the shade index in red (`index * 60`) with alpha 255. ## Composition `client/CharacterSprite.cpp` (C++) and `tools/character_compose.py` (Python, for previews) implement the same steps: 1. skin shade layer in the skin tone, 2. tee and shorts shade layers (always under everything; recolored when they are the chosen top / bottom), 3. body fixed pixels, 4. every chosen item by slot z order (`shoes < bottom < top < hair < hat`): shade layer in the chosen color, then its fixed pixels. A color becomes 4 shades multiplicatively (`colorRamp` / `ramp`): x0.50 with a cool tint, x0.73, the color itself, and a warm highlight 34% toward cream. Keep both implementations in sync. ## Commands ```sh python3 tools/character_art.py bases # base bodies + walk (cached) python3 tools/character_art.py items [ids..] # missing item edits (resumable) python3 tools/character_art.py henry # Professor Henry + portrait python3 tools/character_art.py build # extract layers, write the manifest (free) python3 tools/character_art.py preview # contact sheets in art_src/preview/ python3 tools/pixellab_client.py # ledger spend + account balance ``` Paid jobs are recorded in `art_src/.pending/` before waiting, so an interrupted run resumes the same jobs instead of paying again. To add an item, add it to `ITEMS` in `tools/character_art.py`, run `items ` and `build`.