Cart binary format
.sprite carts are TLV-structured binary blobs. Total cart size is capped at 32 KB. Source code, when present, ships in a separate SRC section that sits outside the cap.
Header (80 bytes)
offset size field
------ ---- ----------
0 4 magic = "SPRT"
4 2 version (u16 LE; v1 today)
6 1 machine_id (u8; today only M8 = 0x01)
7 1 palette_id (u8; 0=PICO-8, 1=DB16, 2=Sweetie 16)
8 2 flags (u16 LE; reserved, 0 in v1)
10 1 target_medium (u8; 0=flat32k, 1=micro-8k, 2=flash-2m)
11 1 reserved_1[1] (all zero)
12 32 title (NUL-padded UTF-8)
44 32 author (NUL-padded UTF-8)
76 4 reserved_2 (all zero)
magic— fixed bytes"SPRT".version— cart format version (u16 LE; currently 1).machine_id— target machine; today onlyM8(0x01).palette_id— 0 = PICO-8, 1 = DB16, 2 = Sweetie 16. Wasreserved_0pre-P3.6.flags— u16 LE; reserved, all zero in v1.title,author— 32 bytes each, NUL-padded UTF-8.target_medium— u8; selects the cart's storage medium / size cap:0= flat 32 KB (M8 default),1= micro-8k (8 KB EEPROM),2= flash-2m (2 MB banked flash). Was the first byte ofreserved_1pre-Task 2.1.reserved_1[1],reserved_2— all zero; reserved for future header growth without bumping the format version.
TLV sections
Each section: 4-byte tag + 4-byte LE length + payload.
| Tag | Cap | Contents |
|---|---|---|
CODE | 16 KB | Compiled bytecode (Op stream + word table). |
SPRT | 8 KB | Sprite sheet — up to 256 tiles × 8×8 × 4-bit indices. |
SOND | 8 KB | Tracker data — instruments + 32×5 patterns + tempo. |
SYMS | 256 B | Symbol table — exported lifecycle word names + bytecode addresses. Runner reads init (run-once at load) and update (per-frame). See below. |
META | — | JSON metadata — formerly carried update word address; SYMS owns that now. Retained for back-compat and future cart-level metadata. |
RAMI | 8 KB | RAM-init bytes for s"" string literals (P2.4). |
SRC | unbounded | Original Forth source — outside the 32 KB cap. Used by /studio's Load .sprite. |
SYMS — lifecycle vocabulary
The SYMS section maps reserved Forth word names to bytecode addresses. Each entry is {name_len:u8, name:[u8;name_len], addr:u16 LE}. The runner consults SYMS at cart load to wire two lifecycle hooks:
init— called once at cart load, before the first frame. Optional; useful for one-time RAM setup or state seeding.update— called every frame at 60 Hz. Required for most carts (a cart withoutupdatestill presents frames but runs no per-frame code).
Other names in the vocabulary are reserved for future hooks (on-input, on-pause, on-save, etc.). Compilers ignore non-vocabulary names; the section is capped at 256 B (~7 entries with 32-byte names; many more with short names).
Carts that pre-date SYMS carry update's address in META JSON as {"update": <addr>}. The runner falls back to that path when SYMS is absent so older carts keep working.
Total size budget
32,768-byte hard cap covers header + all capped TLV sections (CODE, SPRT, SOND, SYMS, META, RAMI). SRC sits outside this cap and may grow arbitrarily.