Mimic
A 48×48 mimic in the treasure chest’s design language, slightly warped: the belly bows out and breathes, the planks run crooked, uneven teeth line the lid seam, a tongue lolls over the front and one eye glints in the gap. Like the chest, the four facings are one mimic turned on the floor and lit from the top-left in world space. Each facing has “idle” (12 frames: the lid breathes 1–2px open, the belly swells, the tongue sways, a glint runs along the teeth) and “bite” (10-frame loop: the lid snaps wide, lunges and chomps shut with a squash).
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.
// Mimic — 48x48, top-down 3/4, key light top-left. The treasure chest's design language (same plank box, barrel// lid on a back hinge, iron bands, brass caps and lock), slightly warped: the belly bows out and breathes, the// planks run crooked, uneven teeth line the lid seam, a tongue lolls over the front and one eye glints in the gap.// Built like chest.js: every pixel casts the oblique view ray (screen y = BASE - z - K*depth) into a few convex// solids, so the four facings are one mimic turned on the floor, lit top-left in world space (right is not a// mirrored left). Teeth are flat tooth sheets on the seam planes; the tongue is a chain of spheres.// Variants (f = down / up / left / right):// idle-f loop, 12f — lid breathes 1-2px open, belly swells, tongue sways, a glint runs along the teeth// bite-f loop, 10f — lid snaps wide (teeth rows, palate, tongue lashing up), lunges, chomps shut with a squashThe last lines. submit declares the canvas and lists the variants the renderer exports.
submit(Mimic, { size: [48, 48], origin: "top-left", kind: "icon", scale: 1, animate: { fps: 10, duration: N_IDLE / 10, loop: true }, anchor: { x: 24, y: GROUND }, variants: [ ...FACES.map((f) => ({ name: `idle-${f}`, args: ["idle", f] })), ...FACES.map((f) => ({ name: `bite-${f}`, args: ["bite", f], animate: { fps: 10, duration: N_BITE / 10, loop: true } })), ],});Show the whole program (442 lines)
// Mimic — 48x48, top-down 3/4, key light top-left. The treasure chest's design language (same plank box, barrel// lid on a back hinge, iron bands, brass caps and lock), slightly warped: the belly bows out and breathes, the// planks run crooked, uneven teeth line the lid seam, a tongue lolls over the front and one eye glints in the gap.// Built like chest.js: every pixel casts the oblique view ray (screen y = BASE - z - K*depth) into a few convex// solids, so the four facings are one mimic turned on the floor, lit top-left in world space (right is not a// mirrored left). Teeth are flat tooth sheets on the seam planes; the tongue is a chain of spheres.// Variants (f = down / up / left / right):// idle-f loop, 12f — lid breathes 1-2px open, belly swells, tongue sways, a glint runs along the teeth// bite-f loop, 10f — lid snaps wide (teeth rows, palate, tongue lashing up), lunges, chomps shut with a squashconst CX = 24.5, GROUND = 45, K = 0.4;const L = 31, D = 19, H = 12, WALL = 3, R = 5;const HL = L / 2, HD = D / 2, T = 1.6, LIDZ = H + WALL, BX = 8;const N_IDLE = 12, N_BITE = 10, WIDE = 66;const LV = norm3([-0.6, -0.25, 0.76]);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]; }const 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 },};// ---- solidsconst 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 - 8, H - 3];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 ANY = [-99, 99, -99, 99, -99, 99];const EYE_X = -5.25, EYE_Q = quad([EYE_X, -HD + 2.6, H + 0.8], [1.9, 1.4, 1.6]);const BOUND_C = [-HL - 1.1, HL + 1.1, -HD - 5, HD + 1.1, 0, H + 11];const BOUND_L = [-HL - 0.4, HL + 0.4, -HD - 0.4, HD + 0.1, H - 3.8, LIDZ + R + 0.1];// teeth: flat sheets on the seam planes. [centre along the edge, half-width, length]; uneven on purposeconst UPPER_FRONT = [[-13.6, 1.1, 2.2], [-11, 1.4, 4.4], [-8, 1.5, 2.7], [-2, 1.5, 2.9], [1.5, 1.5, 2.5], [5, 1.5, 3], [8, 1.5, 2.6], [11, 1.4, 4.2], [13.6, 1.1, 2.3]]; // fangs at +-11, a gap at x=-5 where the eye looks outconst LOWER_FRONT = [-11.5, -8.5, -2, 1, 7.5, 10.5].map((c, i) => [c, 1.5, 2.2 + 0.7 * hash(i, 11)]);const UPPER_SIDE = [-7.5, -4, -0.5].map((c, i) => [c, 1.7, 2.6 + 0.7 * hash(i, 13)]);const LOWER_SIDE = [-5.75, -2.25, 1.25].map((c, i) => [c, 1.5, 2.2 + 0.5 * hash(i, 17)]);// [axis of the sheet plane, plane value, outward sign, edge axis, teeth, grows down (upper) or up (lower)]const UPPER_SHEETS = [[1, -HD - 0.25, -1, 0, UPPER_FRONT], [0, HL + 0.25, 1, 1, UPPER_SIDE], [0, -HL - 0.25, -1, 1, UPPER_SIDE]];const LOWER_SHEETS = [[1, -HD + 0.9, -1, 0, LOWER_FRONT], [0, HL - 0.9, 1, 1, LOWER_SIDE], [0, -HL + 0.9, -1, 1, LOWER_SIDE]];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;}function 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]]);}// ray vs one tooth sheet: returns [t, tooth index] or null. `down` teeth hang from z=H, others rise from it.// Seen edge-on (the ray runs along the sheet) a tooth is a 1px sliver: march the ray through the sheet's thickness.function toothAt(teeth, s, h) { if (h < 0) return -1; for (let i = 0; i < teeth.length; i++) { const [c, w, len] = teeth[i], u = Math.abs(s - c); if (u < w && h <= len * (1 - u / w)) return i; } return -1;}function sheetHit(o, d, sh, down) { const [ax, at, , ea, teeth] = sh; if (d[ax] < 1e-6 && d[ax] > -1e-6) { if (Math.abs(o[ax] - at) > 0.6) return null; const zA = down ? H - 4.6 : H, zB = down ? H : H + 3.2; // tooth band heights const tA = (zB - o[2]) / d[2], tB = (zA - o[2]) / d[2]; // d[2] < 0: the ray descends for (let t = Math.min(tA, tB); t <= Math.max(tA, tB); t += 0.25) { const i = toothAt(teeth, o[ea] + t * d[ea], down ? H - (o[2] + t * d[2]) : o[2] + t * d[2] - H); if (i >= 0) return [t, i]; } return null; } const t = (at - o[ax]) / d[ax]; const i = toothAt(teeth, o[ea] + t * d[ea], down ? H - (o[2] + t * d[2]) : o[2] + t * d[2] - H); return i >= 0 ? [t, i] : null;}// the tongue: a chain of spheres along 4 control points, between hanging (u=0) and lashing up (u=1)function tongueChain(u, sw) { const lerp3 = (a, b) => a.map((v, i) => v + (b[i] - v) * u); const P = [ lerp3([2, -1, H - 2.5], [1, 1, H - 2]), lerp3([4, -HD + 0.4, H + 0.6], [2, -3, H + 4]), lerp3([4.5 + 0.6 * sw, -HD - 1.4, H - 3.5], [3 + 1.5 * sw, -HD, H + 9]), lerp3([5 + 1.8 * sw, -HD - 1.8, H - 8], [1 + 3 * sw, -HD - 3, H + 11]), ]; const out = []; for (let k = 0; k < 9; k++) { const f = (k / 8) * 3, i = Math.min(2, Math.floor(f)), a = f - i; const c = P[i].map((v, j) => v + (P[i + 1][j] - v) * a); const rad = 2.8 - 1.0 * (k / 8); out.push(quad(c, [rad, rad * 0.8, rad * 0.85])); } return out;}function castMimic(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, 0, 0]), ez = F.toL([0, 0, 1]); const tongue = tongueChain(st.tongue, st.sway); const proj = (q) => { const w = F.toW([q[0], q[1] - st.lunge, q[2]]); const sy = BASE - st.hop - w[2] * st.sz - K * w[1] * st.sx; const zf = GROUND - sy - st.hop, bow = 1 + st.bulge * Math.sin(Math.PI * Math.max(0, Math.min(1, zf / (H + 4)))); return [CX + w[0] * st.sx * bow - 0.5, sy - 0.5, w[1] * st.sx]; }; // screen rectangle of a (finite) box in the 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] = proj(lid ? lidToChest([x, y, z], st.th) : [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]) - 2, Math.ceil(r[1]) + 2, Math.floor(r[2]) - 2, Math.ceil(r[3]) + 2]; }; const qbox = (q) => q.c.flatMap((c, i) => [c - 1 / Math.sqrt(q.inv2[i]), c + 1 / Math.sqrt(q.inv2[i])]); const tb = [99, -99, 99, -99, 99, -99]; for (const q of tongue) { const b = qbox(q); for (let i = 0; i < 6; i += 2) { tb[i] = Math.min(tb[i], b[i]); tb[i + 1] = Math.max(tb[i + 1], b[i + 1]); } } const R_BODY = rect(BODY, false), R_LOCK = rect(LOCK, false), R_EYE = rect(qbox(EYE_Q), false), R_TONGUE = rect(tb, false); const R_HANDLES = HANDLES.map((h) => rect(h, false)), R_KNUCKLES = KNUCKLES.map((k) => rect(k, false)); const R_LOWER = rect([-HL, HL, -HD, HD, H, H + 3.4], false), R_UPPER = rect([-HL - 0.6, HL + 0.6, -HD - 0.6, HD, H - 4.6, H], true); const R_LID = rect([-HL, HL, -HD, HD, H, LIDZ + R], true); const o = [0, 0, 0], ol = [0, 0, 0]; const buf = new Array(48 * 48).fill(null); for (let py = 0; py < 48; py++) { // the warp: the belly bows out, most at mid-height of the near face const zf = GROUND - 0.5 - py - st.hop, bow = 1 + st.bulge * Math.sin(Math.PI * Math.max(0, Math.min(1, zf / (H + 4)))); for (let px = 0; px < 48; px++) { const Xw = (px + 0.5 - CX) / (st.sx * bow), Zw = (BASE - py - 0.5 - st.hop) / st.sz; for (let i = 0; i < 3; i++) o[i] = ex[i] * Xw + ez[i] * Zw; o[1] += st.lunge; 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 inR = (r) => px >= r[0] && px <= r[1] && py >= r[2] && py <= r[3]; const inC = convex(o, d, BOUND_C, null), inL = convex(ol, dl, BOUND_L, null); if (!inC && !inL) continue; let bt = 1e9, bid = null, blid = false, bcode = -1, bq = null, bflip = false, bextra = 0; 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) { 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 (inR(R_EYE)) hit("eye", false, ANY, EYE_Q); if (inR(R_TONGUE)) for (const q of tongue) hit("tongue", false, ANY, q); if (inR(R_LOWER)) for (const sh of LOWER_SHEETS) { const r = sheetHit(o, d, sh, false); if (r && r[0] < bt) { bt = r[0]; bid = "lower"; blid = false; bcode = 2 * sh[0] + (sh[2] > 0 ? 1 : 0); bq = null; bflip = false; bextra = r[1]; } } } if (inL) { if (inR(R_LID)) { hit("lidbox", true, LIDBOX, null); hit("barrel", true, BARREL, BARREL_Q); } if (inR(R_UPPER)) for (const sh of UPPER_SHEETS) { const r = sheetHit(ol, dl, sh, true); if (r && r[0] < bt) { bt = r[0]; bid = "upper"; blid = true; bcode = 2 * sh[0] + (sh[2] > 0 ? 1 : 0); bq = null; bflip = false; bextra = r[1]; } } } 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, bextra); c.lid = blid; c.t = bt; c.d = nw[0] * LV[0] + nw[1] * LV[1] + nw[2] * LV[2]; buf[py * 48 + px] = c; } } return { buf, proj };}// a point in the lid's rest frame -> chest-local, with the lid open by th degreesfunction lidToChest(q, th) { const a = (th * Math.PI) / 180, dy = q[1] - HD, dz = q[2] - H; return [q[0], HD + dy * Math.cos(a) + dz * Math.sin(a), H - dy * Math.sin(a) + dz * Math.cos(a)];}function classify(id, p, n, extra) { 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 === "upper" || id === "lower") return { part: "teeth", mat: "tooth", s: id, k: extra }; if (id === "tongue") return { part: "tongue", mat: "tongue" }; if (id === "eye") return { part: "eye", mat: "eye", x }; if (id === "body") { if (face === "top") return { part: "maw", mat: "gum" }; 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 }; } // crooked planks: the seams slope across the face return { part: "body", mat: "wood", s: face, k: Math.floor((z + 0.09 * u + 0.6) / 4), u, z }; } if (id === "inner") return { part: "maw", mat: "maw", s: face }; if (id === "lock") return { part: "lock", mat: "brass", key: face === "front" && ax < 0.5 && z > 5.5 && z < 7.5 }; if (id === "handle") return { part: "handles", mat: "iron", ring: true }; if (id === "knuckle") return { part: "hinges", mat: "iron", knuckle: true, z }; if (id === "lidbox") { if (face === "under") return { part: "maw", mat: "palate", s: "under", k: Math.floor((y + HD) / 4), x, y }; 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 }; } 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); 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 + 0.012 * x) / (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; const glint = fx.glint ? fx.glint(px, py) : 0; let key; if (b.mat === "wood") { let lv = 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; if (!up && lv > 0) lv = Math.min(3, lv + 1); key = WOOD[Math.max(0, Math.min(3, lv))]; } 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]; } 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; key = b.key ? "ink" : GOLD[lv]; } else if (b.mat === "tooth") { // bone: lit left edge, shaded right edge, dark root in the gum line; the glint turns a tooth white-hot const sameT = (q) => q && q.mat === "tooth" && q.s === b.s && q.k === b.k; let k = "n4"; if (!sameT(lf)) k = b.s === "upper" ? "n5" : "n4"; else if (!sameT(rt)) k = "n3"; if (b.s === "upper" ? !sameT(dn) && sameT(up) : !sameT(up) && sameT(dn)) k = k === "n5" ? "n4" : "n3"; if (glint > 0) k = "n5"; key = k; } else if (b.mat === "tongue") { const lv = b.d > 0.72 ? 3 : b.d > 0.4 ? 2 : b.d > 0.05 ? 1 : 0; key = ["r0", "r1", "r2", "v3"][lv]; if (lv === 3 && samePart(up)) key = "r2"; // only the top rim gets the wet highlight } else if (b.mat === "eye") { const cxp = Math.abs(b.x - EYE_X); // a slit pupil once the eye shows more than a sliver; in the idle gap it is just a hot amber glint const tall = samePart(up) && samePart(dn); key = cxp < 0.5 && tall ? "ink" : fx.eyeGlint && !samePart(up) && !samePart(lf) ? "n5" : cxp < 1.2 ? "g3" : "g2"; } else if (b.mat === "maw") { key = b.s === "top" ? "ink" : b.s === "endL" || b.s === "endR" ? "r0" : samePart(up) ? "v0" : "r0"; } else if (b.mat === "gum") { key = "r0"; } else if (b.mat === "palate") { // roof of the mouth: warm near the teeth, dark down the throat, a groove down the middle, ridges across const seam = (q) => q && q.s === b.s && q.k !== b.k; const deep = (b.y + HD) / D; // 0 at the front edge (teeth), 1 at the hinge (throat) const zone = deep < 0.3 ? 2 : deep < 0.62 ? 1 : 0; const ramp = ["ink", "v0", "r0", "r1"]; key = ramp[zone + 1]; if (seam(dn) || seam(rt) || Math.abs(b.x + 0.5) < 0.6) key = ramp[zone]; } keys[i] = key; } return keys;}const PARTS = ["body", "lid", "maw", "tongue", "eye", "teeth", "bands", "hinges", "handles", "caps", "lock"];function drawMimic(g, face, st, fx) { const fr = castMimic(face, st); const buf = fr.buf, 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 fr;}function 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) { const put = (px, py, k) => { if (visible(buf, px, py, depth, 1)) g.pixel(px, py, k); }; put(x, y, "n5"); for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) put(x + dx, y + dy, size > 1 ? "n5" : "n4"); if (size > 1) for (const [dx, dy] of [[2, 0], [-2, 0], [0, 2], [0, -2]]) put(x + dx, y + dy, "n4");}// ---- statesconst BITE = [ // th, sx, sz, hop, lunge, bulge [3.5, 1, 1, 0, 0, 0.04], [16, 0.98, 1.05, 0, 0, 0.05], // the lid starts to rise, the body stretches [50, 0.97, 1.07, 1, 0.6, 0.05], [WIDE, 1, 1.04, 2, 1.5, 0.06], // wide open, lunging: teeth rows, palate, tongue lashing up [WIDE + 2, 1, 1.02, 2, 1.5, 0.06], [30, 1, 1, 1, 1, 0.05], // snapping down [0, 1.07, 0.86, 0, 0.5, 0.09], // CHOMP: squash [0, 0.97, 1.05, 0, 0, 0.03], // rebound [2, 1.01, 0.98, 0, 0, 0.04], [3, 1, 1, 0, 0, 0.04],];const Mimic = sprite(pal, (g, t, state, face) => { contactShadow(g, 24, GROUND, FACINGS[face].shadow, 2); if (state === "idle") { const breath = 0.5 - 0.5 * Math.cos(TAU * t); const st = { th: 3.5 + 3 * breath, sx: 1, sz: 1, hop: 0, lunge: 0, bulge: 0.035 + 0.03 * breath, tongue: 0, sway: Math.sin(TAU * t) }; // a glint runs along the teeth left to right, then rests const gp = 10 + (t / 0.7) * 30; const fr = drawMimic(g, face, st, { glint: t < 0.7 ? (x, y) => (Math.abs(x + (y - 30) * 0.6 - gp) < 1.8 ? 1 : 0) : null, eyeGlint: t > 0.55 && t < 0.9, }); g.detached("glint", () => { if (t >= 0.7) return; // a star pops at the tip of each fang as the glint passes it for (const [c, , len] of UPPER_FRONT) { if (len < 3) continue; const [sx, sy, dep] = fr.proj(lidToChest([c, -HD - 0.3, H - len + 0.8], st.th)); const x = Math.round(sx), y = Math.round(sy); if (Math.abs(x + (y - 30) * 0.6 - gp) < 1.8) star(g, fr.buf, x, y, dep - 1, 1); } }); return; } const f = Math.min(N_BITE - 1, Math.round(t * N_BITE)); const [th, sx, sz, hop, lunge, bulge] = BITE[f]; const st = { th, sx, sz, hop, lunge, bulge, tongue: Math.min(1, th / WIDE), sway: Math.sin(TAU * t + 1) }; const fr = drawMimic(g, face, st, { eyeGlint: f >= 2 && f <= 4 }); if (f === 6) { // chomp: clack marks burst from the front corners of the seam g.detached("clack", () => { for (const s of [-1, 1]) { const [sx0, sy0, dep] = fr.proj([s * (HL + 1), -HD - 1, H + 0.5]); const x = Math.round(sx0), y = Math.round(sy0); for (let k = 1; k <= 2; k++) { for (const [dx, dy] of [[s * (k + 1), -k], [s * (k + 2), 0], [s * (k + 1), k]]) { if (visible(fr.buf, x + dx, y + dy, dep - 3, 0)) g.pixel(x + dx, y + dy, k === 1 ? "n5" : "n4"); } } } }); }});const FACES = ["down", "up", "left", "right"];submit(Mimic, { size: [48, 48], origin: "top-left", kind: "icon", scale: 1, animate: { fps: 10, duration: N_IDLE / 10, loop: true }, anchor: { x: 24, y: GROUND }, variants: [ ...FACES.map((f) => ({ name: `idle-${f}`, args: ["idle", f] })), ...FACES.map((f) => ({ name: `bite-${f}`, args: ["bite", f], animate: { fps: 10, duration: N_BITE / 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.