Treasure chest
A 48×48 treasure chest, modelled as a few solid shapes (a plank box, a barrel lid on a back hinge, brass and iron fittings), and every pixel is found by looking into that model. So the four facings are the same chest turned on the floor, each lit from the top-left in world space, and the lid swings on its real hinge in every view. Each facing has “closed” (12 frames, a glint walks across the brass), “open” (8 frames, once: squash, hop, the lid swings past rest and settles as gold light spills out) and “opened” (12 frames, a coin heap and a gem, sparkles and rising motes).
6×
Get the animated sprites: Arcane Curiosities Vol.1 on itch.io12 animated props · 52 animations
The header. The comment at the top of the file is the brief: size, view, light direction and every variant.
// Treasure chest — 48x48, top-down 3/4, key light top-left.// ONE chest, modelled as a few convex solids (plank box with a hollow, barrel lid on a back hinge, brass and iron// fittings), and every pixel is found by casting the oblique view ray (screen y = BASE - z - K*depth) into it.// So the four facings are the same chest turned on the floor, each lit top-left in WORLD space (right is not a// mirrored left), and the lid swings on its real hinge in every view. Tones come from the surface normal through// 4-step material ramps; plank seams, lit lips, rivets and brass rims are countable marks added from the surface// coordinates and the pixel's neighbours (no per-pixel noise).// Variants (f = down / up / left / right):// closed-f idle loop, 12f — a glint walks across the brass// open-f one-shot, 8f — squash, hop, lid swings past rest and settles, gold light spills out// opened-f idle loop, 12f — coin heap + gem, sparkles twinkle, motes riseThe last lines. submit declares the canvas and lists the variants the renderer exports.
submit(Chest, { size: [48, 48], origin: "top-left", kind: "icon", scale: 1, animate: { fps: 10, duration: 1.2, loop: true }, anchor: { x: 24, y: GROUND }, variants: [ ...FACES.map((f) => ({ name: `closed-${f}`, args: ["closed", f] })), ...FACES.map((f) => ({ name: `open-${f}`, args: ["open", f], animate: { fps: 10, duration: N_OPEN / 10, loop: false } })), ...FACES.map((f) => ({ name: `opened-${f}`, args: ["opened", f] })), ],});Show the whole program (423 lines)
// Treasure chest — 48x48, top-down 3/4, key light top-left.// ONE chest, modelled as a few convex solids (plank box with a hollow, barrel lid on a back hinge, brass and iron// fittings), and every pixel is found by casting the oblique view ray (screen y = BASE - z - K*depth) into it.// So the four facings are the same chest turned on the floor, each lit top-left in WORLD space (right is not a// mirrored left), and the lid swings on its real hinge in every view. Tones come from the surface normal through// 4-step material ramps; plank seams, lit lips, rivets and brass rims are countable marks added from the surface// coordinates and the pixel's neighbours (no per-pixel noise).// Variants (f = down / up / left / right):// closed-f idle loop, 12f — a glint walks across the brass// open-f one-shot, 8f — squash, hop, lid swings past rest and settles, gold light spills out// opened-f idle loop, 12f — coin heap + gem, sparkles twinkle, motes riseconst CX = 24.5, GROUND = 45, K = 0.4; // K: screen rise per pixel of depthconst L = 31, D = 19, H = 12, WALL = 3, R = 5; // length, depth, body height, lid wall, barrel riseconst HL = L / 2, HD = D / 2, T = 1.6, LIDZ = H + WALL, BX = 8;const OPEN = 105; // resting angle of the open lid, degreesconst N_IDLE = 12, N_OPEN = 8;const LV = norm3([-0.6, -0.25, 0.76]); // toward the key light (world: X right, Y away, Z up)const WOOD = ["w0", "w1", "w2", "w3"], IRON = ["n0", "n1", "n2", "n3"], GOLD = ["g0", "g1", "g2", "g3"];function norm3(v) { const m = Math.hypot(v[0], v[1], v[2]); return [v[0] / m, v[1] / m, v[2] / m]; }// chest-local (x along the length, y front(-)..back(+), z up) <-> world, per facingconst FACINGS = { down: { toL: (v) => [v[0], v[1], v[2]], toW: (v) => [v[0], v[1], v[2]], ext: HD, shadow: 17 }, up: { toL: (v) => [-v[0], -v[1], v[2]], toW: (v) => [-v[0], -v[1], v[2]], ext: HD, shadow: 17 }, right: { toL: (v) => [v[1], -v[0], v[2]], toW: (v) => [-v[1], v[0], v[2]], ext: HL, shadow: 12 }, left: { toL: (v) => [-v[1], v[0], v[2]], toW: (v) => [v[1], -v[0], v[2]], ext: HL, shadow: 12 },};// ---- solids: boxes [x0,x1,y0,y1,z0,z1], optionally cut by a quadric {c, inv2: 1/r^2 per axis (0 drops the axis)}const quad = (c, r) => ({ c, inv2: r.map((v) => (isFinite(v) ? 1 / (v * v) : 0)) });const BODY = [-HL, HL, -HD, HD, 0, H];const CAV = [-HL + T, HL - T, -HD + T, HD - T, T, 99];const LOCK = [-3.5, 3.5, -HD - 0.8, -HD, H - 6, H];const HANDLES = [[HL, HL + 1, -1.5, 1.5, H - 4, H - 2], [-HL - 1, -HL, -1.5, 1.5, H - 4, H - 2]];const KNUCKLES = [[2.5, 5.5, HD, HD + 1, H - 1, H + 1], [-5.5, -2.5, HD, HD + 1, H - 1, H + 1]];const LIDBOX = [-HL, HL, -HD, HD, H, LIDZ];const BARREL = [-HL, HL, -Infinity, Infinity, LIDZ, Infinity], BARREL_Q = quad([0, 0, LIDZ], [Infinity, HD, R]);const HASP = [-1.5, 1.5, -HD - 1.2, -HD, H - 2.5, H + 2];const HEAP_R = [HL - 1, HD, 9], HEAP_C = [0, 0, H - 4];const HEAP = [-HL + T, HL - T, -HD + T, HD - T, 0, 99], HEAP_Q = quad(HEAP_C, HEAP_R);const heapZ = (x, y) => HEAP_C[2] + HEAP_R[2] * Math.sqrt(Math.max(0, 1 - (x / HEAP_R[0]) ** 2 - (y / HEAP_R[1]) ** 2));const GEM = [-99, 99, -99, 99, -99, 99], GEM_Q = quad([-4, -2, heapZ(-4, -2) + 0.9], [2.8, 2.2, 2.2]);// cheap bounds: if the ray misses both, it misses the chestconst BOUND_C = [-HL - 1.1, HL + 1.1, -HD - 1.1, HD + 1.1, 0, H + 1.1], BOUND_GOLD = [-HL, HL, -HD, HD, 0, H + 8];const BOUND_L = [-HL - 0.1, HL + 0.1, -HD - 1.3, HD + 0.1, H - 2.6, LIDZ + R + 0.1];// ray vs convex solid; no allocation: results in TIN/TOUT and the entry/exit face codes (0..5 slab faces, 6 quadric)let TIN = 0, TOUT = 0, CIN = -1, COUT = -1;function convex(o, d, b, q) { let tin = -1e9, tout = 1e9, cin = -1, cout = -1; for (let i = 0; i < 3; i++) { const lo = b[2 * i], hi = b[2 * i + 1], di = d[i], oi = o[i]; if (lo === -Infinity && hi === Infinity) continue; if (di < 1e-9 && di > -1e-9) { if (oi < lo || oi > hi) return false; continue; } let t1 = (lo - oi) / di, t2 = (hi - oi) / di, c1 = 2 * i, c2 = 2 * i + 1; if (t1 > t2) { const k = t1; t1 = t2; t2 = k; c1 = 2 * i + 1; c2 = 2 * i; } if (t1 > tin) { tin = t1; cin = c1; } if (t2 < tout) { tout = t2; cout = c2; } if (tin > tout) return false; } if (q) { const r = q.inv2, c = q.c; const ox = o[0] - c[0], oy = o[1] - c[1], oz = o[2] - c[2]; const A = d[0] * d[0] * r[0] + d[1] * d[1] * r[1] + d[2] * d[2] * r[2]; const B = 2 * (ox * d[0] * r[0] + oy * d[1] * r[1] + oz * d[2] * r[2]); const C = ox * ox * r[0] + oy * oy * r[1] + oz * oz * r[2] - 1; const disc = B * B - 4 * A * C; if (disc < 0) return false; const sq = Math.sqrt(disc), r1 = (-B - sq) / (2 * A), r2 = (-B + sq) / (2 * A); if (r1 > tin) { tin = r1; cin = 6; } if (r2 < tout) { tout = r2; cout = 6; } if (tin > tout) return false; } TIN = tin; TOUT = tout; CIN = cin; COUT = cout; return true;}// outward normal of face `code` at point pfunction normalOf(code, p, q) { if (code < 6) { const n = [0, 0, 0]; n[code >> 1] = code & 1 ? 1 : -1; return n; } return norm3([(p[0] - q.c[0]) * q.inv2[0], (p[1] - q.c[1]) * q.inv2[1], (p[2] - q.c[2]) * q.inv2[2]]);}// ---- one frame of the chest: cast every pixel, then classify and tonefunction castChest(face, st) { const F = FACINGS[face], BASE = GROUND - K * F.ext; const a = (st.th * Math.PI) / 180, ca = Math.cos(a), sa = Math.sin(a); const lidNormal = (n) => [n[0], n[1] * ca + n[2] * sa, -n[1] * sa + n[2] * ca]; const d = F.toL([0, 1 / st.sx, -K / st.sz]); const dl = [d[0], d[1] * ca - d[2] * sa, d[1] * sa + d[2] * ca]; const ex = F.toL([1 / st.sx, 0, 0]), ez = F.toL([0, 0, -1 / st.sz]); const o0 = F.toL([(0.5 - CX) / st.sx, 0, (BASE - 0.5 - st.hop) / st.sz]); const pr = (q) => project(F, BASE, st, q); // screen rectangle of a finite box (chest or lid frame): pixels outside it skip that solid entirely const rect = (b, lid) => { const r = [99, -99, 99, -99]; for (const x of [b[0], b[1]]) for (const y of [b[2], b[3]]) for (const z of [b[4], b[5]]) { const [sx, sy] = pr(lid ? [x, HD + (y - HD) * ca + (z - H) * sa, H - (y - HD) * sa + (z - H) * ca] : [x, y, z]); r[0] = Math.min(r[0], sx); r[1] = Math.max(r[1], sx); r[2] = Math.min(r[2], sy); r[3] = Math.max(r[3], sy); } return [Math.floor(r[0]) - 1, Math.ceil(r[1]) + 1, Math.floor(r[2]) - 1, Math.ceil(r[3]) + 1]; }; const R_BODY = rect(BODY, false), R_LOCK = rect(LOCK, false), R_GOLD = rect([-HL, HL, -HD, HD, 0, H + 8], false); const R_HANDLES = HANDLES.map((h) => rect(h, false)), R_KNUCKLES = KNUCKLES.map((k) => rect(k, false)); const R_LID = rect([-HL, HL, -HD, HD, H, LIDZ + R], true), R_HASP = rect(HASP, true); const o = [0, 0, 0], ol = [0, 0, 0]; const bc = st.gold ? BOUND_GOLD : BOUND_C; const buf = new Array(48 * 48).fill(null); for (let py = 0; py < 48; py++) for (let px = 0; px < 48; px++) { for (let i = 0; i < 3; i++) o[i] = o0[i] + px * ex[i] + py * ez[i]; ol[0] = o[0]; ol[1] = HD + (o[1] - HD) * ca - (o[2] - H) * sa; ol[2] = H + (o[1] - HD) * sa + (o[2] - H) * ca; const inC = convex(o, d, BOUND_C, null) || (st.gold && convex(o, d, bc, null)); const inL = convex(ol, dl, BOUND_L, null); if (!inC && !inL) continue; const inR = (r) => px >= r[0] && px <= r[1] && py >= r[2] && py <= r[3]; let bt = 1e9, bid = null, blid = false, bcode = -1, bq = null, bflip = false; const hit = (id, lid, box, q) => { if (convex(lid ? ol : o, lid ? dl : d, box, q) && TIN < bt) { bt = TIN; bid = id; blid = lid; bcode = CIN; bq = q; bflip = false; } }; if (inC) { if (inR(R_BODY) && convex(o, d, BODY, null)) { const tb = TIN, cb = CIN, qx = o[0] + tb * d[0], qy = o[1] + tb * d[1]; if (cb === 5 && Math.abs(qx) < HL - T && Math.abs(qy) < HD - T) { // fell in through the opening: the far inner wall or the floor if (convex(o, d, CAV, null) && TOUT < bt) { bt = TOUT; bid = "inner"; blid = false; bcode = COUT; bq = null; bflip = true; } } else if (tb < bt) { bt = tb; bid = "body"; blid = false; bcode = cb; bq = null; bflip = false; } } if (inR(R_LOCK)) hit("lock", false, LOCK, null); for (let i = 0; i < 2; i++) { if (inR(R_HANDLES[i])) hit("handle", false, HANDLES[i], null); if (inR(R_KNUCKLES[i])) hit("knuckle", false, KNUCKLES[i], null); } if (st.gold && inR(R_GOLD)) { hit("heap", false, HEAP, HEAP_Q); hit("gem", false, GEM, GEM_Q); } } if (inL) { if (inR(R_LID)) { hit("lidbox", true, LIDBOX, null); hit("barrel", true, BARREL, BARREL_Q); } if (inR(R_HASP)) hit("hasp", true, HASP, null); } if (!bid) continue; const oo = blid ? ol : o, dd = blid ? dl : d; const p = [oo[0] + bt * dd[0], oo[1] + bt * dd[1], oo[2] + bt * dd[2]]; let n = normalOf(bcode, p, bq); if (bflip) n = [-n[0], -n[1], -n[2]]; const nw = F.toW(blid ? lidNormal(n) : n); const c = classify(bid, p, n); c.lid = blid; c.t = bt; c.d = nw[0] * LV[0] + nw[1] * LV[1] + nw[2] * LV[2]; c.p = p; buf[py * 48 + px] = c; } return { buf, proj: (q) => project(F, BASE, st, q) };}function project(F, BASE, st, q) { const w = F.toW(q); return [CX + w[0] * st.sx - 0.5, BASE - st.hop - w[2] * st.sz - K * w[1] * st.sx - 0.5, w[1] * st.sx];}function classify(id, p, n) { const x = p[0], y = p[1], z = p[2], ax = Math.abs(x), ay = Math.abs(y); const band = Math.abs(ax - BX) <= 1.5, hinge = ax >= 2.5 && ax <= 5.5; const rivet = Math.abs(ax - BX) < 0.5 && Math.abs((((z % 4) + 4) % 4) - 1.5) < 0.5; const face = n[2] > 0.5 ? "top" : n[2] < -0.5 ? "under" : n[1] < -0.5 ? "front" : n[1] > 0.5 ? "back" : n[0] > 0 ? "endR" : "endL"; const u = face === "front" ? x : face === "back" ? -x : y; const CAP = { part: "caps", mat: "brass" }, BAND = { part: "bands", mat: "iron", rivet }; if (id === "body") { if (face === "top") return band ? BAND : { part: "body", mat: "wood", s: "rim", k: 0, u }; const end = face === "endL" || face === "endR"; if (z < 3.5 && (end ? ay > HD - 3.5 : ax > HL - 3.5)) return CAP; if (!end && band) return BAND; if (face === "back" && hinge && z > H - 4) return { part: "hinges", mat: "iron", rivet: Math.abs(ax - 4) < 0.5 && Math.abs(z - 9.5) < 0.5 }; if (end) { const ry = y / 2.5, rz = (z - (H - 6)) / 2, rr = ry * ry + rz * rz; if (rr > 0.3 && rr < 1.1) return { part: "handles", mat: "iron", ring: true }; } return { part: "body", mat: "wood", s: face, k: Math.floor(z / 4), u, z }; } if (id === "inner") return { part: "interior", mat: "dark", s: face }; if (id === "lock" || id === "hasp") return { part: "lock", mat: "brass", key: id === "lock" && face === "front" && ax < 0.5 && z > 7 && z < 9.2 }; if (id === "handle") return { part: "handles", mat: "iron", ring: true }; if (id === "knuckle") return { part: "hinges", mat: "iron", knuckle: true, z }; if (id === "heap") return { part: "gold", mat: "gold" }; if (id === "gem") return { part: "gem", mat: "gem", n }; if (id === "lidbox") { if (face === "under") { if (ax > HL - 1.5 || ay > HD - 1.5) return { part: "bands", mat: "iron", rim: true }; return { part: "lid", mat: "under", s: "under", k: Math.floor((y + HD) / 3.6), u: x, bounce: y > HD - 6 }; } const end = face === "endL" || face === "endR"; if (end ? ay > HD - 3.5 : ax > HL - 3.5) return CAP; if (z < H + 1) return { part: "bands", mat: "iron", rim: true }; if (!end && band) return BAND; if (face === "back" && hinge) return { part: "hinges", mat: "iron" }; return { part: "lid", mat: "wood", s: "wall" + face, k: 0, u }; } // barrel: curved top, or the D-shaped end board if (Math.abs(n[0]) > 0.9) { const e = Math.hypot(y / HD, (z - LIDZ) / R); if (e > 0.8) return { part: "bands", mat: "iron", rim: true }; return { part: "lid", mat: "wood", s: face, k: 0, u: y }; } const phi = Math.atan2((z - LIDZ) / R, y / HD); // 0 = back edge, PI = front edge if (band) return BAND; if (hinge && phi < 0.45) return { part: "hinges", mat: "iron" }; return { part: "lid", mat: "wood", s: "barrel", k: Math.floor(phi / (Math.PI / 3)), u: x };}function level(d) { return d > 0.78 ? 3 : d > 0.45 ? 2 : d > 0.1 ? 1 : 0; }function tone(buf, fx) { const keys = new Array(48 * 48).fill(null); for (let i = 0; i < 48 * 48; i++) { const b = buf[i]; if (!b) continue; const px = i % 48, py = (i - px) / 48; const up = py > 0 ? buf[i - 48] : null, dn = py < 47 ? buf[i + 48] : null; const lf = px > 0 ? buf[i - 1] : null, rt = px < 47 ? buf[i + 1] : null; const samePart = (q) => q && q.part === b.part; let key; if (b.mat === "wood" || b.mat === "under") { let lv = b.mat === "under" ? (b.bounce ? 2 : 1) + (fx.glow || 0) : level(b.d); const seam = (q) => q && q.s === b.s && q.k !== b.k; if (seam(rt) || seam(dn)) lv = 0; else if (seam(up) || seam(lf)) lv = Math.min(3, lv + 1); else if (b.z !== undefined && Math.floor(b.z) % 4 === 2 && hash(b.k * 3 + (b.s === "front" ? 0 : 11), Math.floor((b.u + 40) / 4)) > 0.62 && Math.floor(b.u + 40) % 4 !== 0) lv = Math.max(0, lv - 1); if (!b.lid && up && up.lid && b.s !== "top") lv = 0; // the lid's lip shades the body's top plank if (!up && lv > 0) lv = Math.min(3, lv + 1); // the top silhouette catches the light key = WOOD[Math.max(0, Math.min(3, lv))]; if (fx.backlit && b.lid && !up) key = "g2"; // gold glow behind the lid rims its top edge } else if (b.mat === "iron") { let lv = level(b.d); if (!samePart(lf) && !b.rim) lv = Math.min(3, lv + 1); else if (!samePart(rt) && !b.rim) lv = Math.max(0, lv - 1); if (b.rim && !samePart(up)) lv = Math.min(3, lv + 1); if (b.ring) lv = Math.max(lv, 2) + (!samePart(up) || !samePart(lf) ? 1 : 0) - (!samePart(dn) ? 1 : 0); if (b.knuckle) lv = b.z > H ? 3 : 1; lv = Math.max(0, Math.min(3, lv)); key = b.rivet ? "n4" : lv === 3 && (b.d > 0.85 || (b.knuckle && !samePart(lf))) ? "n4" : IRON[lv]; if (fx.backlit && b.lid && !up) key = "g1"; } else if (b.mat === "brass") { const same = (q) => q && q.mat === "brass"; let lv = b.d > 0.5 ? 2 : 1; if (!same(up)) lv = b.d > 0.1 ? 3 : 2; else if (!same(lf)) lv = 2; else if (!same(dn) || !same(rt)) lv = 0; if (fx.glint) lv = Math.min(3, lv + fx.glint(px, py)); key = b.key ? "ink" : GOLD[lv]; } else if (b.mat === "gold") { let lv = b.d > 0.55 ? 2 : b.d > 0.2 ? 1 : 0; const row = Math.floor(py / 2), col = Math.floor((px + (row & 1) * 1.5) / 3); const cy = py % 2, cxp = Math.floor(px + (row & 1) * 1.5) % 3; if (cy === 0 && cxp === 1) lv += 1; else if (cy === 1 && cxp === 2) lv -= 1; if (hash(col, row) > 0.86 && cy === 0) lv = 3; key = GOLD[Math.max(0, Math.min(3, lv + (fx.glow || 0)))]; } else if (b.mat === "gem") { const f = b.n; // faceted: quantise the normal const dd = Math.round(f[0] * 2) * -0.5 + Math.round(f[2] * 2) * 0.6 - Math.round(f[1] * 2) * 0.2; key = dd > 1.0 ? "b3" : dd > 0.3 ? "b2" : dd > -0.4 ? "b1" : "b0"; } else if (b.mat === "dark") { key = b.s === "top" ? "w0" : b.d > 0.2 ? "w1" : "w0"; } keys[i] = key; } return keys;}const PARTS = ["body", "lid", "interior", "gold", "gem", "bands", "hinges", "handles", "caps", "lock"];function drawChest(g, face, st, fx) { const { buf, proj } = castChest(face, st); const keys = tone(buf, fx); let x0 = 47, x1 = 0, y0 = 47, y1 = 0; const seen = new Set(); // only declare the parts this view actually shows (an occluded part is not a silent one) for (let i = 0; i < 48 * 48; i++) if (buf[i]) { const x = i % 48, y = (i - x) / 48; x0 = Math.min(x0, x); x1 = Math.max(x1, x); y0 = Math.min(y0, y); y1 = Math.max(y1, y); seen.add(buf[i].part); } g.outlined("ink", () => { for (const part of PARTS) if (seen.has(part)) g.part(part, () => g.field({ x: [x0, x1], y: [y0, y1] }, (x, y) => { const b = buf[y * 48 + x]; return b && b.part === part ? keys[y * 48 + x] : null; })); }); return { buf, proj };}// ---- effectsfunction visible(buf, x, y, depth, slack) { if (x < 0 || y < 0 || x > 47 || y > 47) return false; const b = buf[y * 48 + x]; return !b || depth < b.t + slack;}function star(g, buf, x, y, depth, size, slack) { const put = (px, py, k) => { if (visible(buf, px, py, depth, slack)) g.pixel(px, py, k); }; if (size === 0) { put(x, y, "g2"); return; } put(x, y, size > 1 ? "n5" : "g3"); for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) put(x + dx, y + dy, size > 1 ? "g3" : "g2"); if (size > 1) for (const [dx, dy] of [[2, 0], [-2, 0], [0, 2], [0, -2]]) put(x + dx, y + dy, "g2");}// light pouring out of the opening: rays fanned from just under the rim, blocked by the lid and wallsfunction lightRays(g, face, st, frame, amount) { if (amount <= 0) return; const a = (st.th * Math.PI) / 180, ca = Math.cos(a), sa = Math.sin(a); const inLid = (p) => { const q = [p[0], HD + (p[1] - HD) * ca - (p[2] - H) * sa, H + (p[1] - HD) * sa + (p[2] - H) * ca]; if (q[0] < -HL || q[0] > HL || q[1] < -HD || q[1] > HD || q[2] < H) return false; if (q[2] <= LIDZ) return true; return (q[1] / HD) ** 2 + ((q[2] - LIDZ) / R) ** 2 <= 1; }; g.detached("light", () => { for (let i = 0; i < 8; i++) { // eight shafts rising from a ring just under the rim, leaning outward: they fan in every facing const ang = (i / 8) * TAU + 0.35; const origin = [Math.cos(ang) * 9, Math.sin(ang) * 4 - 0.5, H - 1]; const dir = norm3([Math.cos(ang) * 0.3, Math.sin(ang) * 0.3 - 0.15, 1]); const len = amount * (15 + 8 * hash(i, 3)) * (i % 2 ? 0.65 : 1); for (let r = 0; r < len; r += 0.35) { const p = [origin[0] + dir[0] * r, origin[1] + dir[1] * r, origin[2] + dir[2] * r]; if (inLid(p)) break; if (p[2] < H + 0.5) continue; const [sx, sy, dep] = frame.proj(p); const x = Math.round(sx), y = Math.round(sy); if (!visible(frame.buf, x, y, dep, 0.5)) continue; const k = r / len; if (k > 0.7 && !chk(x, y)) continue; g.pixel(x, y, k < 0.3 ? "g3" : k < 0.7 ? "g2" : "g1"); } } });}// [x, y, height above the heap, phase]: some sit on the coins, some hang in the air above themconst SPARKS = [[-8, -2, 0.4, 0.0], [7, 2, 0.4, 0.25], [1, -4, 0.4, 0.5], [-3, 3, 0.4, 0.75], [11, -3, 5, 0.62], [-12, 1, 7, 0.12], [4, -1, 9, 0.37], [-5, 0, 12, 0.87]];function sparkles(g, frame, t) { // the treasure's glow: three faint shafts breathing above the coins (dithered, never outlined) g.detached("glow", () => { for (let k = 0; k < 3; k++) { const x0 = [-7, 1, 8][k], y0 = [-3, 3, -1][k]; const len = 7 + 3 * Math.sin(TAU * (t + k / 3)); for (let r = 0; r < len; r += 0.5) { const [sx, sy, dep] = frame.proj([x0 + r * 0.12 * (k - 1), y0, heapZ(x0, y0) + 1 + r]); const x = Math.round(sx), y = Math.round(sy); if (!chk(x, y) || !visible(frame.buf, x, y, dep, 0.5)) continue; g.pixel(x, y, r < 2 ? "g2" : "g1"); } } }); g.detached("sparkles", () => { for (const [x, y, h, ph] of SPARKS) { const p = (t + ph) % 1; if (p > 0.5) continue; const [sx, sy, dep] = frame.proj([x, y, heapZ(x, y) + h]); const size = p < 0.08 || p > 0.42 ? 0 : p < 0.17 || p > 0.33 ? 1 : 2; star(g, frame.buf, Math.round(sx), Math.round(sy), dep, size, 1.5); } }); g.detached("motes", () => { for (let k = 0; k < 3; k++) { const p = (t + k / 3) % 1; const x = [-6, 3, 9][k] + Math.round(Math.sin(TAU * (p + k * 0.3)) * 1.5), y = [-1, 2, -2][k]; const [sx, sy, dep] = frame.proj([x, y, H + 2 + p * 19]); const px = Math.round(sx), py = Math.round(sy); if (p < 0.8 && visible(frame.buf, px, py, dep, 0.5)) g.pixel(px, py, p < 0.45 ? "g3" : "g2"); } });}// ---- statesconst REST = { th: 0, sx: 1, sz: 1, hop: 0, gold: false };const OPEN_KEYS = [ // th, sx, sz, hop, light [0, 1, 1, 0, 0], [0, 1.04, 0.9, 0, 0], // anticipation: the chest squats [9, 0.97, 1.06, 1, 0.35], // springs up a pixel, lid cracks, first light [48, 1, 1, 0, 1], [90, 1, 1, 0, 1], [118, 1, 1, 0, 0.8], // swings past rest [99, 1, 1, 0, 0.45], [OPEN, 1, 1, 0, 0], // == opened, frame 0];const Chest = sprite(pal, (g, t, state, face) => { contactShadow(g, 24, GROUND, FACINGS[face].shadow, 2); if (state === "closed") { // a glint band sweeps the brass left to right over 3/4 of the loop, then rests off-chest const gp = -6 + (t / 0.75) * 62; const glint = (x, y) => { const q = x + (y - 30) * 0.6 - gp; return Math.abs(q) < 0.8 ? 3 : Math.abs(q) < 2 ? 1 : 0; }; const fr = drawChest(g, face, REST, { glint: t < 0.75 ? glint : null }); // where the band crosses a lit brass rim, a small star flashes g.detached("glint", () => { for (let i = 0; i < 48 * 48; i++) { const b = fr.buf[i]; if (!b || b.mat !== "brass") continue; const x = i % 48, y = (i - x) / 48, up = fr.buf[i - 48]; if (t < 0.75 && (!up || up.mat !== "brass") && Math.abs(x + (y - 30) * 0.6 - gp) < 0.5 && b.part === "lock") star(g, fr.buf, x, y - 1, -99, 1, 0); } }); return; } if (state === "open") { const f = Math.min(N_OPEN - 1, Math.round(t * N_OPEN)); const [th, sx, sz, hop, light] = OPEN_KEYS[f]; const st = { th, sx, sz, hop, gold: th > 0 }; const fr = drawChest(g, face, st, { glow: light > 0.6 ? 1 : 0, backlit: face === "up" && th > 60 }); lightRays(g, face, st, fr, light); if (f === N_OPEN - 1) sparkles(g, fr, 0); return; } const st = { th: OPEN, sx: 1, sz: 1, hop: 0, gold: true }; const fr = drawChest(g, face, st, { backlit: face === "up" }); sparkles(g, fr, t);});const FACES = ["down", "up", "left", "right"];submit(Chest, { size: [48, 48], origin: "top-left", kind: "icon", scale: 1, animate: { fps: 10, duration: 1.2, loop: true }, anchor: { x: 24, y: GROUND }, variants: [ ...FACES.map((f) => ({ name: `closed-${f}`, args: ["closed", f] })), ...FACES.map((f) => ({ name: `open-${f}`, args: ["open", f], animate: { fps: 10, duration: N_OPEN / 10, loop: false } })), ...FACES.map((f) => ({ name: `opened-${f}`, args: ["opened", f] })), ],});These excerpts omit the boilerplate prepended by the asset pack build script: a single-line runtime header, the shared palette, and common helper utilities (contactShadow, litIndex, flame, hash, chk, TAU). The displayed line numbers match the original source files. Each program was authored by an AI coding agent and adjusted after visual inspection of the rendered frames.
Have your agent write programs like this.
Tessarune gives it the drawing API reference, 25 production manuals and a renderer to check its work against.