Tessarune
Dungeon Mechanisms · clay_pot.js

Clay pot

32×32 · 418 lines of JavaScript

A 32×32 breakable terracotta urn with a painted band of cream dots, drawn from one hollow model so the same shape gives the resting pot, every pose of the hit and the jagged broken base. “idle” (8 frames) slides a glint down the lit shoulder; “hit” (6 frames, once) squashes on impact and rocks on its foot; “break” (10 frames, once) cracks and bursts into 6 shards and 3 coins that arc out and land, with a dust puff; “broken” (8 frames) leaves the base, shards and coins on the floor, a coin glinting. The shards are the pot’s own pixels, cut along straight edges.

Clay pot — Rendered output6×
Rendered by this program, variant “break” · 32×32 · 10 frames
Dungeon MechanismsGet the animated sprites: Dungeon Mechanisms on itch.ioVol.2 · 12 traps, doors and breakables

The header. The comment at the top of the file is the brief: size, view, light direction and every variant.

clay_pot.js · 1–12
// Clay pot (breakable terracotta urn) — 32x32, top-down 3/4, key light top-left.// The urn is a hollow solid of revolution, ray-marched per pixel (camera elevation: ellipse ratio 0.35, as in// Vol.1), so the same model gives the resting pot, every squash/rock pose of the hit, and the jagged broken base// (a height cut around the axis, open so the dark inside shows). Tones come from the surface normal through a// terracotta ramp (w0 w1 w2 r2 + r3 sheen); the painted band and its cream dots are countable marks.// Shards are the SAME pixels as the intact pot, cut along straight Voronoi edges, so what flies is visibly the pot;// once a shard is off the pot it is re-lit as a chunk (lit top-left edge, dark lower edge) so it reads on the floor.// Variants://   idle    loop 8f      — a sheen glint slides down the lit shoulder//   hit     one-shot 6f  — squash on impact, rocks right/left on its foot, settles on idle frame 0//   break   one-shot 10f — cracks, bursts into 6 shards + 3 coins that arc out and land; dust puff; ends on broken f0//   broken  loop 8f      — base, shards and coins on the floor; a coin glints

The last lines. submit declares the canvas and lists the variants the renderer exports.

clay_pot.js · 409–418
submit(Pot, {  size: [W, H], origin: "top-left", kind: "icon", scale: 1,  anchor: { x: CX, y: GROUND },  variants: [    { name: "idle", args: ["idle"], animate: { fps: 10, duration: N_IDLE / 10, loop: true } },    { name: "hit", args: ["hit"], animate: { fps: 10, duration: N_HIT / 10, loop: false } },    { name: "break", args: ["break"], animate: { fps: 10, duration: N_BREAK / 10, loop: false } },    { name: "broken", args: ["broken"], animate: { fps: 10, duration: N_BROKEN / 10, loop: true } },  ],});
Show the whole program (418 lines)
clay_pot.js · 418 lines
// Clay pot (breakable terracotta urn) — 32x32, top-down 3/4, key light top-left.// The urn is a hollow solid of revolution, ray-marched per pixel (camera elevation: ellipse ratio 0.35, as in// Vol.1), so the same model gives the resting pot, every squash/rock pose of the hit, and the jagged broken base// (a height cut around the axis, open so the dark inside shows). Tones come from the surface normal through a// terracotta ramp (w0 w1 w2 r2 + r3 sheen); the painted band and its cream dots are countable marks.// Shards are the SAME pixels as the intact pot, cut along straight Voronoi edges, so what flies is visibly the pot;// once a shard is off the pot it is re-lit as a chunk (lit top-left edge, dark lower edge) so it reads on the floor.// Variants://   idle    loop 8f      — a sheen glint slides down the lit shoulder//   hit     one-shot 6f  — squash on impact, rocks right/left on its foot, settles on idle frame 0//   break   one-shot 10f — cracks, bursts into 6 shards + 3 coins that arc out and land; dust puff; ends on broken f0//   broken  loop 8f      — base, shards and coins on the floor; a coin glintsconst W = 32, H = 32, CX = 16, REFY = 27.6, GROUND = 29;const SE = 0.35, CE = Math.sqrt(1 - SE * SE);          // camera elevation (sin, cos)const HTOP = 20.6, WALL = 1.1, FLOOR = 1.2;const LIGHT = (() => { const v = [-0.55, 0.5, 0.68], m = Math.hypot(...v); return v.map((c) => c / m); })();   // view space: right, up, toward cameraconst N_IDLE = 8, N_HIT = 6, N_BREAK = 10, N_BROKEN = 8;const TER = ["w0", "w1", "w2", "r2", "r3"];// ---- profile: radius along the height (foot, belly, shoulder, neck, flared lip)const PROF = [[0, 4.4], [0.8, 5.0], [2.5, 6.6], [5, 8.0], [7.5, 8.5], [10, 8.1], [12.5, 6.9], [14.5, 5.3], [16.2, 3.9],  [17.4, 3.5], [18.6, 3.9], [19.6, 5.0], [20.6, 5.4]];const wallAt = (h) => WALL + Math.max(0, h - 18.8) * 1.3;   // the lip thickens into a flat rolled rimconst RT = [];for (let i = 0; i <= 420; i++) {  const h = i * 0.05;  let r = PROF[PROF.length - 1][1];  for (let k = 1; k < PROF.length; k++) {    const [h0, r0] = PROF[k - 1], [h1, r1] = PROF[k];    if (h <= h1) { const u = (h - h0) / (h1 - h0), s = u * u * (3 - 2 * u); r = r0 + (r1 - r0) * (0.5 * u + 0.5 * s); break; }  }  RT.push(r);}const R = (h) => RT[Math.max(0, Math.min(420, Math.round(h * 20)))];const dR = (h) => (R(h + 0.4) - R(h - 0.4)) / 0.8;// jagged break line of the base (height as a function of the angle around the axis; 0 = facing the camera)const CUT = (th) => {  const k = ((th / TAU) * 6 + 10.15) % 1;                 // 6 teeth around  return 3.1 + (k < 0.5 ? k : 1 - k) * 4.2;};// ---- ray march: world (x right, h up, z toward camera) -> object via the posefunction toObj(x, h, z, T) {  const x1 = x - T.dx - T.piv, h1 = h - T.hop;  const c = Math.cos(T.ang), s = Math.sin(T.ang);  const xo = x1 * c - h1 * s + T.piv, ho = x1 * s + h1 * c;  return [xo / T.sx, ho / T.sy, z / T.sx];}function nToView(n, T) {  let a = n[0] / T.sx, b = n[1] / T.sy, c3 = n[2] / T.sx;  const m = Math.hypot(a, b, c3); a /= m; b /= m; c3 /= m;  const c = Math.cos(T.ang), s = Math.sin(T.ang);  const wx = a * c + b * s, wh = -a * s + b * c;  return [wx, wh * CE - c3 * SE, wh * SE + c3 * CE];}function solid(x, h, z, cut) {  if (h < 0 || h > HTOP) return null;  const rho = Math.hypot(x, z), r = R(h);  if (rho > r) return null;  const rin = r - wallAt(h);  if (h > FLOOR && rho < rin) return null;  const th = Math.atan2(x, z);  if (cut && h > CUT(th)) return null;  // which surface did the ray enter through: the nearest one  let s = "out", best = r - rho;  if (HTOP - h < best) { s = "top"; best = HTOP - h; }  if (h > FLOOR) { if (rho - rin < best) { s = "in"; best = rho - rin; } }  else if (rho < rin && FLOOR - h < best) { s = "floor"; best = FLOOR - h; }  if (cut && CUT(th) - h < best) { s = "cut"; best = CUT(th) - h; }  return { s, rho, th, h, x, z };}function cast(T, cut) {  const buf = new Array(W * H).fill(null);  for (let py = 0; py < H; py++) for (let px = 0; px < W; px++) {    const X = px + 0.5 - CX, Y = py + 0.5 - REFY;    if (Math.abs(X - T.dx) > 11) continue;    for (let d = 16; d >= -16; d -= 0.15) {      const wh = -Y * CE + d * SE, wz = Y * SE + d * CE;      const [xo, ho, zo] = toObj(X, wh, wz, T);      const hit = solid(xo, ho, zo, cut);      if (hit) { buf[py * W + px] = hit; break; }    }  }  return tone(buf, T, cut);}// ---- tones.  Each pixel keeps its ramp level (lv) and material (m) so shards can be re-lit later.function level(d) { return d > 0.93 ? 4 : d > 0.56 ? 3 : d > 0.2 ? 2 : d > -0.15 ? 1 : 0; }function bandKey(lv) { return lv >= 3 ? "r1" : lv >= 1 ? "r0" : "w0"; }function tone(buf, T, cutMode) {  const out = new Array(W * H).fill(null);  for (let i = 0; i < W * H; i++) {    const b = buf[i]; if (!b) continue;    const px = i % W, py = (i - px) / W;    const up = py > 0 ? buf[i - W] : null;    if (b.s === "out") {      const n = nToView([b.x / (b.rho || 1), -dR(b.h), b.z / (b.rho || 1)], T);      const d = n[0] * LIGHT[0] + n[1] * LIGHT[1] + n[2] * LIGHT[2];      let lv = level(d);      if (b.h < 1.3 && lv > 0) lv -= 1;                                   // the foot sits in its own shadow      if (lv === 4 && !(b.h > 6.5 && b.h < 11)) lv = 3;                    // sheen only on the belly      if (!up && lv >= 2 && lv < 4) lv += 1;                               // the top silhouette catches the light      if (b.h > 11.6 && b.h < 13.9) {                                      // painted band (dark slip)        let key = bandKey(lv), m = "band";        const a = ((b.th / (TAU / 12)) % 1 + 1) % 1;                        // a cream dot every 30 degrees        if (Math.abs(b.h - 12.75) < 0.6 && Math.abs(a - 0.5) < 0.17 && d > -0.3) { key = lv >= 2 ? "w4" : "w3"; m = "dot"; }        out[i] = { key, part: "band", m, lv, h: b.h, th: b.th, ext: true };      } else out[i] = { key: TER[lv], part: "pot", m: "clay", lv, h: b.h, th: b.th, ext: true };    } else if (b.s === "top") {      // the rolled rim: bright on the lit (left / far) side, mid on the right      const lv = b.x - b.z * 0.6 < -1 ? 4 : b.x < 2.2 ? 3 : 2;      out[i] = { key: TER[lv], part: "pot", m: "clay", lv, h: b.h, th: b.th, ext: true, rim: true };    } else if (b.s === "cut") {      out[i] = { key: b.x < 2 ? "w3" : "w2", part: "pot", m: "cut", lv: 3, h: b.h, th: b.th, ext: true };    } else if (b.s === "in") {      // inner wall: the far side faces the camera; its right half catches the light over the rim      out[i] = { key: b.x > 0.5 && b.z < 0 ? "w1" : "w0", part: "inside", m: "in", lv: 0, h: b.h, th: b.th, ext: false };    } else {      out[i] = { key: "w0", part: "inside", m: "in", lv: 0, h: b.h, th: b.th, ext: false };    }  }  if (cutMode) {    // the broken bowl sits lower in the light: lift its outer wall one step, then draw the broken edge as one    // pale jagged line (fresh clay): outer wall pixels with nothing (or the inside) above them, and the far wall's top    const snap = out.map((o) => (o ? { part: o.part } : null));    for (let i = 0; i < W * H; i++) {      const o = out[i]; if (!o) continue;      if (o.m === "clay" && o.h > 0.9) { o.lv = Math.min(3, o.lv + 2); o.key = TER[o.lv]; }      if (i < W) continue;      const a = snap[i - W], x = i % W;      if (o.part !== "inside") {        if (!a || a.part === "inside") { o.key = x < CX - 1 ? "w4" : x < CX + 4 ? "w3" : "w2"; o.m = "cut"; o.part = "pot"; }      } else if (!a) { o.key = x < CX ? "w3" : "w2"; o.part = "pot"; o.m = "cut"; }    }  }  return out;}// ---- compositing: my own occupancy buffer mirrors what the renderer holds, so overlapping chunks get an ink seamfunction markOcc(occ, idx) {  occ[idx] = 1;  const x = idx % W, y = (idx - x) / W;  for (const [nx, ny] of [[x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]]) if (nx >= 0 && ny >= 0 && nx < W && ny < H) occ[ny * W + nx] = 1;}function emitLayer(g, occ, name, pix, detached) {  // pix: Map idx -> key  const seam = [];  for (const idx of pix.keys()) {    const x = idx % W, y = (idx - x) / W;    for (const [nx, ny] of [[x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]]) {      if (nx < 0 || ny < 0 || nx >= W || ny >= H) continue;      const k = ny * W + nx;      if (!pix.has(k) && occ[k]) seam.push(k);    }  }  for (const k of seam) pix.set(k, "ink");  let x0 = W, x1 = -1, y0 = H, y1 = -1;  for (const idx of pix.keys()) { const x = idx % W, y = (idx - x) / W; x0 = Math.min(x0, x); x1 = Math.max(x1, x); y0 = Math.min(y0, y); y1 = Math.max(y1, y); }  if (x1 < 0) return;  const body = () => g.outlined("ink", () => g.field({ x: [x0, x1], y: [y0, y1] }, (x, y) => pix.get(y * W + x) ?? null));  if (detached) g.detached(name, body); else g.part(name, body);  for (const idx of pix.keys()) markOcc(occ, idx);}function drawPot(g, occ, buf, over) {  // intact pot (or the base): one outlined body, emitted per part so the report lists them  let x0 = W, x1 = -1, y0 = H, y1 = -1;  for (let i = 0; i < W * H; i++) if (buf[i]) { const x = i % W, y = (i - x) / W; x0 = Math.min(x0, x); x1 = Math.max(x1, x); y0 = Math.min(y0, y); y1 = Math.max(y1, y); }  const keyAt = (i) => (over && over.has(i) ? over.get(i) : buf[i].key);  g.outlined("ink", () => {    for (const p of ["pot", "band", "inside"]) {      if (!buf.some((b) => b && b.part === p)) continue;      g.part(p, () => g.field({ x: [x0, x1], y: [y0, y1] }, (x, y) => {        const b = buf[y * W + x];        return b && b.part === p ? keyAt(y * W + x) : null;      }));    }  });  for (let i = 0; i < W * H; i++) if (buf[i]) markOcc(occ, i);}// ---- poses (cached)const REST = { sx: 1, sy: 1, ang: 0, piv: 0, hop: 0, dx: 0 };const CACHE = {};const intact = () => CACHE.intact || (CACHE.intact = cast(REST, false));const baseBuf = () => CACHE.base || (CACHE.base = cast(REST, true));const HIT_KEYS = [  // sx, sy, ang(deg), pivot side, dx  [1.1, 0.9, 0, 0, 0],  [0.95, 1.06, 9, 1, 1],  [1, 1, -6, -1, 0],  [1, 1, 3.5, 1, 0],  [1.02, 0.98, -1.5, -1, 0],  [1, 1, 0, 0, 0],];function hitPose(f) {  const key = "hit" + f;  if (CACHE[key]) return CACHE[key];  const [sx, sy, deg, side, dx] = HIT_KEYS[f];  if (f === N_HIT - 1) return (CACHE[key] = intact());  return (CACHE[key] = cast({ sx, sy, ang: (deg * Math.PI) / 180, piv: side * 4.4, hop: 0, dx }, false));}// ---- shards: exterior pixels above the break line, split by straight Voronoi edgesconst SEEDS = [[16, 10], [16, 21], [10, 19], [22, 19]];function shards() {  if (CACHE.shards) return CACHE.shards;  const buf = intact();  const list = SEEDS.map(() => []);  for (let i = 0; i < W * H; i++) {    const b = buf[i]; if (!b || !b.ext || b.h <= CUT(b.th) + 0.15) continue;    const x = i % W, y = (i - x) / W;    let best = 0, bd = 1e9;    SEEDS.forEach(([sx, sy], k) => { const d = (x - sx) ** 2 + ((y - sy) * 1.15) ** 2; if (d < bd) { bd = d; best = k; } });    list[best].push([x, y, b]);  }  const out = list.map((px) => {    let cx = 0, cy = 0;    for (const [x, y] of px) { cx += x + 0.5; cy += y + 0.5; }    cx /= px.length; cy /= px.length;    const m = new Map(px.map(([x, y, b]) => [`${x},${y}`, b]));    return { px, m, cx, cy };  });  return (CACHE.shards = out);}// crack pixels for the impact frame: pot pixels whose right or lower neighbour belongs to another shardfunction crackMap() {  if (CACHE.cracks) return CACHE.cracks;  const owner = new Map();  shards().forEach((s, k) => { for (const [x, y] of s.px) owner.set(y * W + x, k); });  const m = new Map();  for (const [i, k] of owner) {    const x = i % W;    const r = owner.get(i + 1), d = owner.get(i + W);    if ((x < W - 1 && r !== undefined && r !== k) || (d !== undefined && d !== k)) m.set(i, "w0");  }  return (CACHE.cracks = m);}// where each shard lands: [x, y, spin (deg), landing frame, peak (px), behind the base?, flat (y scale at rest)]const LAND = [  [8, 17, -12, 6, 6, true, 0.8],       // neck + shoulder with the lip and painted band: back left  [25, 18, 85, 6, 6, true, 0.8],       // belly front: back right  [5, 28, -6, 5, 3, false, 0.55],       // belly left: front left  [27, 28, 6, 5, 3, false, 0.55],       // belly right: front right];const COINS = [  // start x (mouth), rest x, rest y, landing frame, peak  [15, 16, 30, 6, 7],                 // rolls out in front  [17, 17, 26, 8, 6],                 // falls back into the broken bowl (the one that glints)];// re-light a shard as a chunk: top/left edge lit, bottom/right edge dark, interior keeps its pot tone (clamped)function chunkKey(b, pix, x, y, lit) {  const has = (dx, dy) => pix.has((y + dy) * W + (x + dx));  let lv = Math.max(1, Math.min(3, b.lv + lit));  if (!has(0, -1) || !has(-1, 0)) lv = Math.min(4, lv + 1);  else if (!has(0, 1) || !has(1, 0)) lv = Math.max(0, lv - 1);  if (b.m === "band") return bandKey(lv);  if (b.m === "dot") return lv >= 2 ? "w4" : "w3";  if (b.m === "cut") return lv >= 3 ? "w3" : "w2";  return TER[lv];}function shardPixels(s, tx, ty, deg, lit, sy) {  const a = (deg * Math.PI) / 180, c = Math.cos(a), sn = Math.sin(a);  const src = new Map();  const r = 8, bx = Math.round(tx), by = Math.round(ty);  for (let y = by - r; y <= by + r; y++) for (let x = bx - r; x <= bx + r; x++) {    if (x < 0 || y < 0 || x >= W || y >= H) continue;    const dx = x + 0.5 - tx, dy = (y + 0.5 - ty) / (sy || 1);    const lx = dx * c + dy * sn, ly = -dx * sn + dy * c;    const b = s.m.get(`${Math.floor(lx + s.cx)},${Math.floor(ly + s.cy)}`);    if (b) src.set(y * W + x, b);  }  if (lit === null) { const m = new Map(); for (const [k, b] of src) m.set(k, b.key); return m; }  const pix = new Map();  for (const [k, b] of src) { const x = k % W, y = (k - x) / W; pix.set(k, chunkKey(b, src, x, y, lit)); }  return pix;}function shardState(k, f) {  const s = shards()[k], [rx, ry, spin, lf, peak, , flat] = LAND[k];  if (f <= 0) return { x: s.cx, y: s.cy, a: 0, lit: null, sy: 1 };  const u = Math.min(1, (f - 1) / (lf - 1)), e = 1 - (1 - u) * (1 - u);  // burst: the first moving frame pushes every piece out from the pot's centre  const ox = s.cx - 16, oy = s.cy - 17, ol = Math.hypot(ox, oy) || 1;  const x0 = s.cx + (ox / ol) * 2, y0 = s.cy + (oy / ol) * 1.5;  let x = x0 + (rx - x0) * e, y = y0 + (ry - y0) * u - peak * 4 * u * (1 - u);  if (f === lf + 1) y -= 1;                               // one small bounce after landing  return { x, y, a: spin * e, lit: f === 1 ? null : 0, sy: 1 + (flat - 1) * u };}function coin(g, occ, name, x, y, face) {  // face: 0 = lying (3x2), 1 = spinning face-on (3x3 disc), 2 = edge-on (1x3)  const pix = new Map();  const put = (px, py, k) => { if (px >= 0 && py >= 0 && px < W && py < H) pix.set(py * W + px, k); };  if (face === 0) { put(x - 1, y, "g2"); put(x, y, "g3"); put(x + 1, y, "g2"); put(x - 1, y + 1, "g1"); put(x, y + 1, "g1"); put(x + 1, y + 1, "g0"); }  else if (face === 1) { put(x, y - 1, "g2"); put(x - 1, y, "g3"); put(x, y, "g2"); put(x + 1, y, "g1"); put(x, y + 1, "g0"); }  else { put(x, y - 1, "g3"); put(x, y, "g2"); put(x, y + 1, "g1"); }  emitLayer(g, occ, name, pix, true);}function star(g, x, y, size) {  g.detached("glint", () => {    if (size === 0) { g.pixel(x, y, "g3"); return; }    g.pixel(x, y, "n5");    for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) g.pixel(x + dx, y + dy, "g3");  });}function dust(g, f) {  // two soft dust clouds roll out from the foot: each is three round clumps, a small n4 core in a checker of n3,  // drifting outward and up, thinning to a sparse n2 dither and gone  const k = f - 1;  if (k < 0 || k > 4) return;  const CLUMPS = [[0, 0, 2.0, 0], [-2.6, 0.7, 1.5, 1], [2.3, -1.0, 1.4, 1]];   // outward, down, radius, dither parity  g.detached("dust", () => {    const seen = new Set();    for (const s of [-1, 1]) for (const [ox, oy, r0, par] of CLUMPS) {      const r = r0 + k * 0.4, cx = CX + s * (10.5 + k * 1.7 + ox), cy = GROUND - 1.2 - k * 0.8 + oy;      for (let py = Math.floor(cy - r - 1); py <= cy + r + 1; py++) for (let px = Math.floor(cx - r - 1); px <= cx + r + 1; px++) {        const d = Math.hypot(px + 0.5 - cx, (py + 0.5 - cy) * 1.1) / r;        if (d > 1 || px < 0 || px >= W || py < 0 || py >= H || seen.has(py * W + px)) continue;        const on = ((px + py + par) & 1) === 0;        let key = null;        if (k === 0) key = d < 0.5 ? "n4" : on ? "n3" : null;        else if (k === 1) key = d < 0.35 ? "n4" : on ? (d < 0.7 ? "n3" : "n2") : null;        else if (k === 2) key = on ? (d < 0.55 ? "n3" : "n2") : null;        else if (k === 3) key = on && (px + 2 * py) % 3 !== 0 ? "n2" : null;        else key = on && (px + 2 * py) % 4 === 0 ? "n2" : null;        if (key) { seen.add(py * W + px); g.pixel(px, py, key); }      }    }  });}// sheen glint: slides down the highlight on the lit belly over idle frames 2..5function glintPath() {  if (CACHE.glint) return CACHE.glint;  const buf = intact(), hl = [];  for (let i = 0; i < W * H; i++) if (buf[i] && buf[i].lv === 4 && buf[i].part === "pot") hl.push(i);  hl.sort((a, b) => a - b);  const pick = (u) => hl[Math.min(hl.length - 1, Math.round(u * (hl.length - 1)))];  return (CACHE.glint = [null, null, pick(0), pick(0.35), pick(0.7), pick(1), null, null]);}function drawShard(g, occ, k, st) {  emitLayer(g, occ, "shard" + k, shardPixels(shards()[k], st.x, st.y, st.a, st.lit, st.sy), true);}function drawBroken(g, occ, f) {  const rest = (k) => ({ x: LAND[k][0], y: LAND[k][1], a: LAND[k][2], lit: 0, sy: LAND[k][6] });  const order = LAND.map((l, k) => k).sort((a, b) => LAND[a][1] - LAND[b][1]);  for (const k of order) if (LAND[k][5]) drawShard(g, occ, k, rest(k));        // behind the base  drawPot(g, occ, baseBuf());  for (const k of order) if (!LAND[k][5]) drawShard(g, occ, k, rest(k));  COINS.forEach(([, x, y], i) => coin(g, occ, "coin" + i, x, y, 0));  if (f !== null) {    const [x, y] = [COINS[1][1], COINS[1][2]];    if (f === 3 || f === 5) star(g, x, y - 1, 0);    if (f === 4) star(g, x, y - 1, 1);  }}const Pot = sprite(pal, (g, t, mode) => {  const occ = new Uint8Array(W * H);  const fb = Math.min(N_BREAK - 1, Math.round(t * N_BREAK));  // the shadow widens as the pieces spread over the floor  contactShadow(g, CX, GROUND, mode === "broken" || (mode === "break" && fb >= 5) ? 14 : mode === "break" && fb >= 2 ? 12 : 10, 2);  if (mode === "idle") {    const f = Math.round(t * N_IDLE) % N_IDLE;    const over = new Map(), gp = glintPath()[f];    if (gp !== null && gp !== undefined) { over.set(gp, "n5"); over.set(gp - W, "r3"); over.set(gp + W, "r3"); }    drawPot(g, occ, intact(), over);    return;  }  if (mode === "hit") {    const f = Math.min(N_HIT - 1, Math.round(t * N_HIT));    drawPot(g, occ, hitPose(f));    if (f === 0) star(g, 27, 15, 1);    if (f === 1) g.detached("chips", () => { g.pixel(28, 13, "n4"); g.pixel(29, 17, "n4"); g.pixel(27, 11, "n3"); });    return;  }  if (mode === "break") {    const f = Math.min(N_BREAK - 1, Math.round(t * N_BREAK));    if (f === N_BREAK - 1) { drawBroken(g, occ, 0); return; }    if (f === 0) { drawPot(g, occ, cast({ sx: 1.08, sy: 0.93, ang: 0, piv: 0, hop: 0, dx: 0 }, false), crackMap()); star(g, 27, 15, 1); return; }    const S = shards().map((s, k) => ({ k, st: shardState(k, f) }));    S.sort((a, b) => a.st.y - b.st.y);    const behind = (k) => LAND[k][5] && f >= LAND[k][3];    for (const { k, st } of S) if (behind(k)) drawShard(g, occ, k, st);    drawPot(g, occ, baseBuf());    for (const { k, st } of S) if (!behind(k)) drawShard(g, occ, k, st);    COINS.forEach(([sx, rx, ry, lf, peak], i) => {      const u = Math.min(1, (f - 1) / (lf - 1));      const x = Math.round(sx + (rx - sx) * u), y = Math.round(12 + (ry - 12) * u - peak * 4 * u * (1 - u));      coin(g, occ, "coin" + i, x, y, u >= 1 ? 0 : (f + i) % 2 ? 1 : 2);    });    dust(g, f);    return;  }  drawBroken(g, occ, Math.round(t * N_BROKEN) % N_BROKEN);});submit(Pot, {  size: [W, H], origin: "top-left", kind: "icon", scale: 1,  anchor: { x: CX, y: GROUND },  variants: [    { name: "idle", args: ["idle"], animate: { fps: 10, duration: N_IDLE / 10, loop: true } },    { name: "hit", args: ["hit"], animate: { fps: 10, duration: N_HIT / 10, loop: false } },    { name: "break", args: ["break"], animate: { fps: 10, duration: N_BREAK / 10, loop: false } },    { name: "broken", args: ["broken"], animate: { fps: 10, duration: N_BROKEN / 10, loop: true } },  ],});

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.