Tessarune
Arcane Curiosities Vol.1 · alembic.js

Alembic

48×48 · 222 lines of JavaScript

A 48×48 distillation set on a small wooden stand: a round glass flask of brew in a brass ring over a spirit lamp, a copper swan-neck tube and a small conical receiving vial. In the 16-frame loop the lamp flickers, the brew boils, vapour climbs the neck, and each loop one drop swells at the spout, falls into the vial and ripples the distillate. The glass has an ink rim and a hard highlight streak, and the floor shows through wherever it is empty. The brew comes in green, violet and blue by a ramp swap.

Alembic — Rendered output6×
Rendered by this program, variant “idle-green” · 48×48 · 16 frames
Arcane Curiosities Vol.1Get 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.

alembic.js · 1–7
// Alembic (distillation set) on a small wooden stand — 48x48, top-down 3/4, key light top-left.// A round glass flask of brew sits in a brass ring over a spirit lamp; vapour climbs the neck into a copper swan-neck// tube that drips the distillate into a small conical receiving vial.// Loop: 16 frames @ 10 fps. The lamp flickers, the brew boils (bubbles rise and pop), vapour rises into the neck,// and each loop one drop swells at the spout, falls through the vial's neck and ripples the distillate.// Glass language: ink rim, a hard highlight streak, the floor shows through wherever the glass is empty.// Variants: brew colour green / violet / blue (ramp swap — no new colours).

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

alembic.js · 213–222
submit(Alembic, {  size: [48, 48], origin: "top-left", kind: "icon", scale: 1,  animate: { fps: 10, duration: 1.6, loop: true },  anchor: { x: 24, y: 45 },  variants: [    { name: "idle-green" },    { name: "idle-violet", repalette: swap("brew", "v") },    { name: "idle-blue", repalette: swap("brew", "b") },  ],});
Show the whole program (222 lines)
alembic.js · 222 lines
// Alembic (distillation set) on a small wooden stand — 48x48, top-down 3/4, key light top-left.// A round glass flask of brew sits in a brass ring over a spirit lamp; vapour climbs the neck into a copper swan-neck// tube that drips the distillate into a small conical receiving vial.// Loop: 16 frames @ 10 fps. The lamp flickers, the brew boils (bubbles rise and pop), vapour rises into the neck,// and each loop one drop swells at the spout, falls through the vial's neck and ripples the distillate.// Glass language: ink rim, a hard highlight streak, the floor shows through wherever the glass is empty.// Variants: brew colour green / violet / blue (ramp swap — no new colours).const CX = 24, GROUND = 45;const BULB = { cx: 15.5, cy: 20.5, r: 9 };                 // flask bulb (centre on pixel 15)const LEVEL = BULB.cy + 1;                                // brew surface lineconst NECK = { x0: 13, x1: 17, top: 8 };                  // ink columns x0 / x1, glass betweenconst VIAL = { xc: 37.5, rim: 26, bottom: 40, level: 35 };const SPOUT = [37, 21];                                   // last pixel of the tube; the drop hangs below itconst N = 16;                                             // frames per loopconst frameOf = (t) => Math.round(t * N) % N;// ---- glass helpers ---------------------------------------------------------------------------------------const inBulb = (x, y) => Math.hypot(x + 0.5 - BULB.cx, y + 0.5 - BULB.cy) <= BULB.r;const inNeck = (x, y) => x >= NECK.x0 && x <= NECK.x1 && y >= NECK.top && y <= BULB.cy;const inFlask = (x, y) => inBulb(x, y) || inNeck(x, y);const vialHalf = (y) => {  if (y === VIAL.rim) return 3.1;                         // flared lip  if (y <= 30) return 2.1;                                // neck  if (y >= VIAL.bottom) return 4.6;                       // rounded foot  return 2.6 + ((y - 31) / (VIAL.bottom - 1 - 31)) * 2.5; // cone};const inVial = (x, y) => y >= VIAL.rim && y <= VIAL.bottom && Math.abs(x + 0.5 - VIAL.xc) <= vialHalf(y);const rimOf = (inside, x, y) => !inside(x + 1, y) || !inside(x - 1, y) || !inside(x, y + 1) || !inside(x, y - 1);// Glass edge: ink silhouette, then a 1px inner rim of glass (lit on the top-left, neutral elsewhere, so it never// merges with the brew colour of any variant) so the// vessel keeps its shape on a dark floor while the middle stays see-through.function glassEdge(inside, x, y, litTest) {  if (rimOf(inside, x, y)) return "ink";  for (const [ox, oy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {    if (!rimOf(inside, x + ox, y + oy)) continue;    return litTest(ox, oy) ? "n4" : "n2";  }  return null;}function flask(g, t) {  const { cx, cy, r } = BULB;  const rs = Math.sqrt(r * r - (LEVEL - cy) ** 2) - 1.2, rys = 2;   // brew surface ellipse  const inSurface = (px, py) => ((px - cx) / rs) ** 2 + ((py - LEVEL) / rys) ** 2 <= 1;  const glowOn = Math.sin(TAU * t * 2) > 0;                          // lamp light through the brew  g.part("flask", () => g.field({ x: [cx - r - 1, cx + r + 1], y: [NECK.top, cy + r + 1] }, (x, y) => {    if (!inFlask(x, y)) return null;    const px = x + 0.5, py = y + 0.5, dx = px - cx, dy = py - cy, d = Math.hypot(dx, dy);    const brew = py > LEVEL || inSurface(px, py);    const edge = glassEdge(inFlask, x, y, (ox, oy) => (brew ? ox < 0 : ox < 0 || oy < 0));    if (edge === "ink") return edge;    const ang = Math.atan2(dy, dx);    const streak = d > r - 3.4 && d < r - 1.8 && ang < -1.8 && ang > -2.7;          // upper-left highlight arc    if (brew) {      if (edge) return edge === "n4" ? "brew2" : "brew0";                             // glass wall seen through brew      if (inSurface(px, py)) return dx > -6 && dx < -2.5 && Math.abs(py - LEVEL - 0.5) < 0.5 ? "brew3" : "brew2";  // lit surface + glint      if (Math.abs(dx) < 2 && dy > r - 3) return glowOn ? "brew3" : "brew2";         // lamp glow at the bottom      if (dx < -r + 3 && dy < 5) return "brew2";      if (dx > 1.5 && d > r - 3) return "brew0";                                      // far side, in shadow      return "brew1";    }    // empty glass: see-through, only the rim and the highlight    if (streak) return d > r - 2.6 && ang < -2.05 && ang > -2.45 ? "n5" : "b3";    if (edge) return edge;    return null;  }));  g.part("flaskGlint", () => { g.pixel(19, 26, "brew3"); g.pixel(20, 25, "brew2"); });   // inner reflection}function vial(g, t) {  const f = frameOf(t);  const ripple = f >= 12 && f <= 14 ? f - 11 : 0;  g.part("vial", () => g.field({ x: [31, 44], y: [VIAL.rim, VIAL.bottom] }, (x, y) => {    if (!inVial(x, y)) return null;    const px = x + 0.5, dx = px - VIAL.xc, hw = vialHalf(y);    if (y === VIAL.rim && Math.abs(dx) < 2.5 && !rimOf(inVial, x, y - 1) && inVial(x, y)) { /* lip */ }    const edge = glassEdge(inVial, x, y, (ox, oy) => ox < 0 || oy < 0);    if (edge === "ink") return y === VIAL.rim && Math.abs(dx) < 2.6 ? "b3" : "ink";   // lit lip of the mouth    if (y >= VIAL.level) {      if (edge) return edge === "n4" ? "brew3" : "brew0";      if (y === VIAL.level) {        if (ripple && Math.abs(Math.abs(dx) - ripple) < 0.6) return "brew3";            // ripple ring        return "brew3";      }      return dx < -hw + 2.5 ? "brew2" : dx > hw - 2.5 ? "brew1" : "brew2";    }    if (dx < -hw + 2.2 && dx > -hw + 1.2 && y > 30 && y < 34) return y === 32 ? "n5" : "b3";   // highlight    return edge;  }));}function drop(g, t) {  const f = frameOf(t), [sx, sy] = SPOUT;  g.detached("drop", () => {    if (f <= 2) g.pixel(sx, sy + 1, "brew2");                                   // a bead forms...    else if (f <= 5) { g.pixel(sx, sy + 1, "brew2"); g.pixel(sx, sy + 2, "brew1"); }    else if (f <= 8) { g.pixel(sx, sy + 1, "brew3"); g.pixel(sx, sy + 2, "brew2"); g.pixel(sx, sy + 3, "brew1"); }    else if (f <= 11) {                                                          // ...lets go and falls      const y = [sy + 5, sy + 8, sy + 12][f - 9];      g.pixel(sx, y, "brew3"); g.pixel(sx, y + 1, "brew2");    } else if (f === 12) { g.pixel(sx - 1, VIAL.level - 1, "brew3"); g.pixel(sx + 1, VIAL.level - 1, "brew3"); g.pixel(sx, VIAL.level - 2, "brew3"); }  });}function boil(g, t) {  const { cx, cy } = BULB;  g.detached("bubbles", () => {    for (let i = 0; i < 3; i++) {      const p = (t * 2 + i / 3) % 1;      const x = Math.round(cx - 0.5 + [-2, 1, 3][i] + Math.sin(TAU * (p + i * 0.3)) * 0.8);      const y = Math.round(cy + 6 - p * 6.5);      if (p < 0.8) {        g.pixel(x, y, "brew3");        if (p > 0.35) g.pixel(x + 1, y, "brew2");      } else if (p < 0.92) { g.pixel(x - 1, Math.floor(LEVEL) - 1, "brew3"); g.pixel(x + 1, Math.floor(LEVEL) - 1, "brew3"); }    }  });  g.detached("vapour", () => {    for (let i = 0; i < 2; i++) {      const p = (t + i / 2) % 1;      const x0 = cx - 0.5 + (i ? 3 : -3);      const x = Math.round(x0 + (cx - 0.5 - x0) * Math.min(1, p * 1.6));      const y = Math.round(LEVEL - 2 - p * 13);      if (p < 0.4) { g.pixel(x, y, "brew3"); g.pixel(x, y + 1, "brew2"); }      else if (p < 0.8) g.pixel(x, y, "brew2");      else g.pixel(x, y, "brew1");    }  });}function board(g) {  g.part("board", () => {    g.rect({ x: [3, 44], y: [38, 40] }, "w3", { fill: "w3" });           // top face    g.line([3, 41], [44, 41], "w4");                                     // lit front edge    g.rect({ x: [3, 44], y: [42, 43] }, "w2", { fill: "w2" });           // front face    g.line([3, 44], [44, 44], "w1");    g.line([6, 39], [11, 39], "w2"); g.line([27, 38], [31, 38], "w2"); g.line([19, 40], [24, 40], "w2");   // grain    g.line([13, 43], [18, 43], "w1"); g.pixel(34, 42, "w1"); g.line([38, 43], [41, 43], "w1");  });  g.part("feet", () => {    g.rect({ x: [4, 7], y: [45, 45] }, "w0", { fill: "w0" });    g.rect({ x: [40, 43], y: [45, 45] }, "w0", { fill: "w0" });  });}function lamp(g) {  const cx = 15.5, cy = 37.5, rx = 4.6, ry = 2.2;  g.part("lamp", () => {    g.field({ x: [10, 21], y: [35, 40] }, (x, y) => {      const i = litIndex(x, y, cx, cy, rx, ry, 4, -0.1);      return i < 0 ? null : ["g0", "g1", "g1", "g2"][i];    });    g.pixel(12, 36, "g3"); g.pixel(13, 36, "g3");                        // specular    g.line([14, 35], [16, 35], "n2");                                    // wick collar    g.pixel(14, 35, "n4");  });}const RING = { cy: 27, rx: 5.8, ry: 1.7 };                 // support ring under the bulbfunction ringStand(g) {  g.part("standLegs", () => {                                // splayed tripod legs (the third hides behind the lamp)    g.line([9, 28], [5, 39], "g2");    g.line([22, 28], [26, 39], "g1");  });}function ring(g) {  // front arc of the ring the flask rests in; its ends turn back behind the glass  g.part("ring", () => {    for (let x = Math.floor(BULB.cx - RING.rx); x <= Math.ceil(BULB.cx + RING.rx) - 1; x++) {      const u = (x + 0.5 - BULB.cx) / RING.rx, k = Math.sqrt(Math.max(0, 1 - u * u)) * RING.ry;      const y = Math.round(RING.cy + k);      g.pixel(x, y, u < -0.3 ? "g3" : u < 0.4 ? "g2" : "g1");      g.pixel(x, y + 1, "ink");    }  });}function collar(g) {  g.part("collar", () => {    g.rect({ x: [12, 18], y: [6, 8] }, "g1", { fill: "g1" });    g.line([12, 6], [18, 6], "g2"); g.line([12, 7], [13, 7], "g3"); g.pixel(18, 8, "g0");  });}function tube(g) {  g.part("tube", () => {    g.sweep([[17, 6], [22, 3], [29, 3], [34, 7], [36.5, 13], [37, 20]], { w0: 1.5, w1: 1.1 },      (u, v) => {        const w = u < 0.62 ? v : -v;                                     // light comes from the top-left        return w < -0.35 ? "w4" : w > 0.45 ? "w1" : "w3";      });    g.rect({ x: [36, 38], y: [20, 21] }, "g2", { fill: "g2" });          // brass spout    g.pixel(36, 20, "g3");  });}function fire(g, t) {  g.part("flame", () => flame(g, 15, 34, 5, 6, t, 0.3, ["fl0", "fl1", "fl2", "fl3"]));}const Alembic = sprite(pal, (g, t) => {  contactShadow(g, CX, GROUND, 20, 1);  g.outlined("ink", () => { board(g); ringStand(g); lamp(g); });  flask(g, t);  boil(g, t);  ring(g);  g.outlined("ink", () => { fire(g, t); collar(g); tube(g); });  vial(g, t);  drop(g, t);});submit(Alembic, {  size: [48, 48], origin: "top-left", kind: "icon", scale: 1,  animate: { fps: 10, duration: 1.6, loop: true },  anchor: { x: 24, y: 45 },  variants: [    { name: "idle-green" },    { name: "idle-violet", repalette: swap("brew", "v") },    { name: "idle-blue", repalette: swap("brew", "b") },  ],});

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.