- name
- art
- description
- Any game (apps/porthole). Use when adding, changing, or regenerating game art -- Pets Club's dog poses, size variants and icons, or Biscuit's scenes and discovery illustrations. Covers regenerating each game's generated headers, reviewing the output, and what to check before calling it done.
Every game's art is generated from a source tool, never hand-edited -- the generated
header says so in its own comment, and a hand-edit is silently overwritten by the next
`make -C apps/porthole art`. Regenerate, review the visual output, then confirm the game
still builds and runs.
## Pets Club (`apps/porthole/games/pets-club/`)
1. Edit `tools/art.py` only. Never hand-edit `sprites.h`.
2. Regenerate: `make -C apps/porthole art` (runs `python3 games/pets-club/tools/art.py`). This rewrites `sprites.h` and, if Pillow is installed, `build/art/sheet.png`.
3. `python3 games/pets-club/tools/art.py --check` verifies `sprites.h` matches the generator without rewriting it -- `make -C apps/porthole check` (and so pre-commit) runs it. Use it if you want to confirm you didn't forget to regenerate after an `art.py` edit.
4. If Pillow isn't installed, `make -C apps/porthole art` still succeeds (`sprites.h` is written) but prints "Pillow missing; skipping preview." Say so rather than skipping the visual review silently -- install Pillow or ask for a human look instead.
5. Open `build/art/sheet.png` and check, per pose/size row: the 1px dark-brown (`C_DKBROWN`) inner outline is continuous, the dog still faces right, the silhouette reads at the smallest size (`pup`, roughly 24x18 px), and every color used is one of the 32 in `apps/porthole/os/palette.h`.
6. If a pose's proportions changed or a pose was added, the `DOG_PARTS` anchors (`headX/Y/R`, `bodyX/Y/RX/RY`, `tailX/Y`, `eyeX/Y`) shift or gain a row. Report the new numbers explicitly -- `game.cpp` reads them directly for hit-testing and effect placement, and a silent shift breaks gameplay with no compiler error.
7. Confirm nothing else broke: `make -C apps/porthole test && make -C apps/porthole snap` (both pull the regenerated `sprites.h` in transitively).
## Biscuit (`apps/porthole/games/biscuit/`)
1. Scenes and discovery illustrations are generated by the same tool the old standalone firmware used, moved as-is: `tools/art/{art.js, discovery-art*.js, export-assets.mjs}`. Edit those JS sources, never the generated output. `export-assets.mjs` emits `generated/scenes.h` (`SCENES[stage][time][activity][frame]`, RLE-encoded RGB565), `generated/discovery_art.h` (`enum DiscoveryId` plus 96 `Image565` entries in that order), the pixel data itself in `generated/art_data.inc`, and `generated/manifest.json` (a SHA-256 per scene/picture -- 504 scenes + 96 pictures, all equal to the legacy firmware's manifest). `art.cpp` is the one translation unit that `#include`s `art_data.inc` (about 11 MB of source, 4.9 MB of flash, ~1.4 s to build) so the rest of the game never pays that compile cost.
2. Regenerate: `make -C apps/porthole art` runs `node games/biscuit/tools/art/export-assets.mjs` (Node 22+, zero npm packages) alongside `art.py`. `make -C apps/porthole lint` runs `--check`, which reproduces the output byte-for-byte against the manifest -- when `node` isn't on PATH it prints "node not found: skipping ... --check (install Node 22+ to run it)" and exits 0 rather than failing the gate; CI's runner (ubuntu-24.04) has Node 22, so it always actually runs there.
3. Every drawn pixel is a flat fill or a pre-baked scene from this tool -- there is no runtime blending to review beyond what the tool already produced (text is the one exception to "no runtime blending," see the app brief's constraint 2). Review still means opening the regenerated scene/illustration output and checking it against the source JS's intent, the same way the Pets Club sheet gets reviewed.
4. The launcher icon (`generated/icon.h`, from `tools/icon.py`) follows the indexed style of `tools/shell_art.py`, not the RGB565 scenes -- `App::icon()` is always an indexed `gfx::Sprite` regardless of the game's own surface.
5. Fonts are a separate, one-time conversion: `generated/fonts.h` comes from the four bundled LVGL `.c` font files via `apps/porthole/tools/fontconv.py` (a regex over the C arrays; `--game` picks the game), not from `art.js`. Regenerating needs `npx lv_font_conv@1.5.3` and is only necessary if a glyph is ever added -- the content gate (see the `content` skill) rejects any glyph outside the existing set, so this should be rare.
6. Confirm nothing else broke: rerun with `--check` (item 2) plus `make -C apps/porthole test && make -C apps/porthole snap`.
View on GitHub