spritekit builds deterministic, device-sized animation strips from tracked
source PNGs and one tracked asset-set manifest. The source PNGs and manifest are
the source of truth; everything under the adjacent build/ directory is a
reproducible QA output or runtime candidate.
This tool handles transparent sprite-like assets: characters, drones, items,
and detached effects. Opaque parallax backgrounds have a separate composition
contract in assets/README.md; background_builder.py validates their loop
and transition seams and builds a static composition contact sheet.
One asset set has one fixed output cell, explicit source origin, reference scale, transform, and indexed palette. Every animation therefore keeps the same coordinate system, apparent scale, and render anchor. The build fails instead of silently rescaling a large pose.
- Each source PNG is one horizontal strip with exactly the declared frame count.
- Every strip in an asset set has the same source-cell dimensions.
sourceOriginis the one anchor shared by every source frame. The generated output maps it to the bottom-center origin recorded inbuild/manifest.json.- The chroma key comes from the manifest; keyed pixels do not influence the palette.
- Character, drone, and item frames must contain pixels. Effects may opt into empty frames.
syncWithrequires the effect and its owning animation to have identical frame counts, FPS, and playback. The effect uses the same cell, origin, scale, and draw rectangle as the hero, so it can be composed at(0, 0).- Scaling uses nearest-neighbor sampling. All output strips share one indexed palette.
- Source paths must be relative PNG paths contained by the manifest directory.
Detached effects may share an asset set with their character when they require the same coordinates and timing, but they remain separate source and output strips. A scene-owned effect such as arrival owns a separate asset set.
From the repository root:
python3 -m venv tools/spritekit/venv
tools/spritekit/venv/bin/pip install -r tools/spritekit/requirements.txtDependencies are pinned in requirements.txt; the virtual environment is local
and ignored.
Run the complete read-only verification path with:
tools/spritekit/venv/bin/python tools/spritekit/verify.pyIt checks the generated scene contract header and native parser vector before discovering the spritekit tests.
tools/spritekit/venv/bin/python tools/spritekit/process.py \
assets/sprites/hero/hero.json
tools/spritekit/venv/bin/python tools/spritekit/process.py \
assets/sprites/hero/hero.json --checkThe first command writes build/manifest.json, one indexed PNG per animation,
and build/contact_sheet.png. The --check form performs the same validation
and conversion in memory without writing generated files.
Asset-set manifests own only source art, geometry, palette, and animation
timing. assets/scenes/expedition/scene.json is the sole owner of semantic
state, cue, outcome, and presentation mappings.
Validate the shared far loop, transparent near loops, connector bands, and terminal/middle sequence, then build the static composition contact sheet:
tools/spritekit/venv/bin/python tools/spritekit/background_builder.py \
assets/backgrounds/expedition/expedition.json
tools/spritekit/venv/bin/python tools/spritekit/background_builder.py \
assets/backgrounds/expedition/expedition.json --checkThe generated build/contact_sheet.png composites the repeating sequence
terminal → middle → terminal over the shared far layer for static visual
review. Exact seams and parallax parameters are validated from the manifest
and pixel data.
Build or validate the single tracked expedition composition:
tools/spritekit/venv/bin/python tools/spritekit/scene_builder.py \
assets/scenes/expedition/scene.json
tools/spritekit/venv/bin/python tools/spritekit/scene_builder.py \
assets/scenes/expedition/scene.json --checkThe normal build refreshes every referenced asset, validates its tracked manifest and source hashes, and writes:
assets/scenes/expedition/build/contact_sheet.png: integrated state and seam review frames.firmware/stackchan/data/scene/: the generated LittleFS payload containingscene.jsonand raw.idx4resources.firmware/stackchan/test/test_scene_contract/scene_package_test_vector.generated.h: the tracked exact-manifest vector consumed by the production C++ parser test.
The device package uses at most 16 RGB565 palette entries per resource, index
0 transparency where applicable, and high-nibble-first row-major pixel pairs.
Every package key and structural limit comes from
protocol/scene-package.json; resource order and presentation bindings come
from the tracked scene, while animation kind, playback, frames, and rate come
from its pinned asset manifests. The same tracked lineage generates the
firmware catalog. The builder rejects source drift, geometry or origin drift,
invalid indices or byte counts, broken background connectors, unsafe
decision-control overlap, visible content outside the canvas, invalid
hero/drone spatial relationships, duplicate runtime filenames, and packages
over the tracked scene limit. Every screen coordinate comes from the scene
manifest's single layout declaration.
The unversioned scene-package contract also requires separate
USB-power-absent, host-link-absent, synchronizing, uncertain, and fault
prompts, producer-specific ready text, and disjoint reject/approve controls.
Old fields, missing fields, and extra fields are rejected rather than adapted.
Scene HUD text and control labels use only uppercase ASCII letters and spaces
within their fixed byte limits, matching the device's finite pixel alphabet.
QA images and LittleFS installation files are ignored. The native parser vector
is tracked so a fresh checkout can prove Python-builder/C++-parser parity; it
is generated, never edited by hand, and --check rejects drift.
All --check paths prepare outputs in memory and compare without writing.
Only the command without --check invokes the explicit output writers.
Tracked <animation>_src.png strips are editable art sources. Image generation
or manual cleanup may replace them, but spritekit never regenerates or
overwrites them. Run the strict build after every art change.
Stack-chan firmware consumes and validates the generated raw package. Static contact sheets provide build-time visual QA; physical display behavior and cadence are established by the separate firmware and hardware acceptance checks.