Potion shelf
A 64×64 wall prop in front view with the plank tops foreshortened: a two-tier shelf between two side boards, with brass gussets under each plank and seven different glass vessels, from a gold vial and an open green flask to a blue skull-stoppered bottle and a red squat jug. As a wall prop it has no contact shadow; a thin dithered shadow line falls on the wall under each plank instead. In the 16-frame loop a glint sweeps from vessel to vessel, the flask bubbles and steams, and the skull bottle’s liquid swells bright, lighting its eye sockets, then dims.
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.
// Potion shelf — 64x64 wall prop, front view with the plank tops foreshortened (3/4 top-down). Key light top-left.// A two-tier shelf between two side boards, brass gussets under each plank, seven distinct glass vessels:// top: gold vial · green round flask (open, bubbling) · violet heart bottle · red + blue test tubes in a rack// bottom: green herb jar (cork, label) · blue skull-stoppered Erlenmeyer (soul-light) · red squat jug with handle// Wall prop: no contact shadow — a thin dithered shadow line falls on the wall under each plank instead.// Loop: 16 frames @ 10 fps. A glint sweeps left to right from vessel to vessel, the flask bubbles and steams,// the skull bottle's liquid swells bright (its eye sockets light up) and dims.The last lines. submit declares the canvas and lists the variants the renderer exports.
submit(Shelf, { size: [64, 64], origin: "top-left", kind: "icon", scale: 1, animate: { fps: 10, duration: 1.6, loop: true }, anchor: { x: 32, y: 61 }, variants: [{ name: "idle" }],});Show the whole program (330 lines)
// Potion shelf — 64x64 wall prop, front view with the plank tops foreshortened (3/4 top-down). Key light top-left.// A two-tier shelf between two side boards, brass gussets under each plank, seven distinct glass vessels:// top: gold vial · green round flask (open, bubbling) · violet heart bottle · red + blue test tubes in a rack// bottom: green herb jar (cork, label) · blue skull-stoppered Erlenmeyer (soul-light) · red squat jug with handle// Wall prop: no contact shadow — a thin dithered shadow line falls on the wall under each plank instead.// Loop: 16 frames @ 10 fps. A glint sweeps left to right from vessel to vessel, the flask bubbles and steams,// the skull bottle's liquid swells bright (its eye sockets light up) and dims.const FR = 16;const frameOf = (t) => Math.round(t * FR) % FR;const clamp = (v, a, b) => Math.max(a, Math.min(b, v));const inBox = (x, y, x0, x1, y0, y1) => x >= x0 && x <= x1 && y >= y0 && y <= y1;function inEll(x, y, x0, x1, y0, y1) { const nx = (2 * (x - x0) + 1) / (x1 - x0 + 1) - 1, ny = (2 * (y - y0) + 1) / (y1 - y0 + 1) - 1; return nx * nx + ny * ny <= 1;}// ---- glass ---------------------------------------------------------------------------------------------// The glint sweeps the vessels in screen order (left to right across both tiers), 2 frames apart, 3 frames each.const SWEEP = ["vial", "jar", "flask", "skull", "heart", "jug", "tubes"];function glintStep(name, t) { const k = frameOf(t) - 2 * SWEEP.indexOf(name); return k >= 0 && k <= 2 ? k : -1;}const soul = (t) => 0.5 - 0.5 * Math.cos(TAU * t); // 0 -> 1 -> 0 over the loopconst soulLevel = (t) => (soul(t) > 0.72 ? 1 : soul(t) < 0.28 ? -1 : 0);// One glass vessel as ONE tone expression: dark glass rim, liquid under a bright liquid line, a specular// column on the lit (left) side, refracted light pooling low on the far side, and the travelling glint band.function vessel(g, V, t) { const lv = V.level ? V.level(t) : 0; const R = (i) => V.ramp + clamp(i + lv, 0, 3); const k = glintStep(V.name, t); const [x0, x1] = V.box.x, [y0, y1] = V.box.y; const a = x0 + 0.4 * y0, b = x1 + 0.4 * y1; const band = k < 0 ? null : a + (0.2 + 0.3 * k) * (b - a); const I = V.inside; g.field(V.box, (x, y) => { if (!I(x, y)) return null; const eL = !I(x - 1, y), eR = !I(x + 1, y), eT = !I(x, y - 1), eB = !I(x, y + 1); const edge = eL || eR || eT || eB; const solid = V.solid && V.solid(x, y); const wet = y > V.liq && !solid; if (!edge && band !== null && Math.abs(x + 0.4 * y - band) < 0.5) return "n5"; if (!edge && V.spec(x, y)) return wet ? "n5" : "n4"; if (wet) { if (eR || eB) return R(0); if (V.lite && V.lite(x, y)) return R(3); if (V.hot && V.hot(x, y)) return R(2); return R(1); } if (y === V.liq && !solid) return edge ? R(1) : R(3); // the liquid line return edge ? "n1" : "n2"; // empty glass: dark rim, smoky interior });}function lipRow(g, x0, x1, y) { // thick glass lip: lit on the left, shaded on the right for (let x = x0; x <= x1; x++) g.pixel(x, y, x === x0 + 1 ? "n4" : x === x1 ? "n1" : x === x1 - 1 ? "n2" : "n3");}function cork(g, x0, x1, y0, y1) { g.rect({ x: [x0, x1], y: [y0, y1] }, "w2", { fill: "w2" }); g.line([x0, y0], [x1, y0], "w3"); // top face g.pixel(x0, y0, "w4"); g.line([x0, y0 + 1], [x0, y1], "w3"); g.line([x1, y0 + 1], [x1, y1], "w1");}function sparkle(g, x, y, k) { if (k === 1) { g.pixel(x, y, "n5"); for (const [dx, dy] of [[-1, 0], [1, 0], [0, -1], [0, 1]]) g.pixel(x + dx, y + dy, "n4"); } else if (k === 2) g.pixel(x, y, "n5");}// refracted light: the ring of pixels one step inside the far rim of a round vesselconst rimNear = (I, x, y) => !I(x + 2, y) || !I(x, y + 2) || !I(x + 1, y + 1);// ---- the seven vessels ---------------------------------------------------------------------------------const VIAL = { name: "vial", ramp: "g", box: { x: [7, 11], y: [8, 25] }, liq: 12, spark: [11, 9], inside: (x, y) => inBox(x, y, 8, 10, 8, 9) || inBox(x, y, 7, 11, 10, 24) || inBox(x, y, 8, 10, 25, 25), spec: (x, y) => x === 8 && y >= 10 && y <= 21, hot: (x, y) => x === 10 && y >= 20, dress: (g) => { lipRow(g, 7, 11, 7); cork(g, 8, 10, 4, 6); },};const FLASK = { name: "flask", ramp: "e", box: { x: [15, 25], y: [10, 25] }, liq: 18, spark: [24, 15], inside: (x, y) => inEll(x, y, 15, 25, 15, 25) || inBox(x, y, 19, 21, 10, 15), spec: (x, y) => (x === 16 && y >= 19 && y <= 21) || (x === 17 && y === 17), hot: (x, y) => x >= 20 && y >= 20 && rimNear(FLASK.inside, x, y), dress: (g) => { // flared open mouth seen slightly from above: far rim, dark opening, near rim g.line([19, 7], [21, 7], "n3"); g.pixel(18, 8, "n3"); g.line([19, 8], [21, 8], "e0"); g.pixel(22, 8, "n1"); lipRow(g, 18, 22, 9); },};const heartHalf = (y) => (y <= 18 ? 6 : 6 - (y - 18) * 0.6);const HEART = { name: "heart", ramp: "v", box: { x: [29, 41], y: [11, 25] }, liq: 15, spark: [40, 14], inside: (x, y) => inBox(x, y, 34, 36, 11, 15) || inEll(x, y, 29, 33, 15, 20) || inEll(x, y, 37, 41, 15, 20) || (y >= 16 && y <= 25 && Math.abs(x - 35) <= Math.max(1, heartHalf(y))), spec: (x, y) => (x === 30 && y >= 18 && y <= 19) || (x === 31 && (y === 17 || y === 20)), hot: (x, y) => x >= 36 && y >= 21, lite: (x, y) => (x === 38 && y === 17) || (y === 16 && (x === 31 || x === 32)), dress: (g) => { lipRow(g, 33, 37, 10); // round glass stopper, the heart's colour made pale g.ellipse({ x: [33, 37], y: [6, 9] }, "v2", { fill: "v2" }); g.pixel(34, 6, "v3"); g.pixel(33, 7, "v3"); g.pixel(34, 7, "n5"); g.pixel(37, 8, "v1"); g.pixel(36, 9, "v1"); },};const TUBE_A = { name: "tubes", ramp: "r", box: { x: [46, 49], y: [10, 21] }, liq: 13, inside: (x, y) => inBox(x, y, 46, 49, 10, 21), spec: (x, y) => x === 47 && y >= 11 && y <= 19, hot: (x, y) => x === 48 && y >= 18,};const TUBE_B = { name: "tubes", ramp: "b", box: { x: [51, 54], y: [12, 21] }, liq: 16, inside: (x, y) => inBox(x, y, 51, 54, 12, 21), spec: (x, y) => x === 52 && y >= 13 && y <= 19, hot: (x, y) => x === 53 && y >= 18,};const TUBES = { name: "tubes", spark: [55, 14] };const JAR = { name: "jar", ramp: "e", box: { x: [7, 17], y: [41, 53] }, liq: 45, spark: [16, 44], inside: (x, y) => inBox(x, y, 9, 15, 41, 42) || inBox(x, y, 8, 16, 43, 53) || inBox(x, y, 7, 17, 44, 52), spec: (x, y) => x === 8 && y >= 44 && y <= 51, hot: (x, y) => x === 16 && y >= 49, dress: (g) => { lipRow(g, 8, 16, 40); cork(g, 9, 15, 37, 39); g.line([9, 42], [15, 42], "w1"); g.pixel(15, 42, "w0"); g.pixel(16, 43, "w1"); // twine and its tail // herbs steeping in the brew for (const [x, y] of [[10, 51], [11, 52], [14, 52], [15, 51], [12, 46]]) g.pixel(x, y, "e0"); // parchment label with two lines of ink g.rect({ x: [10, 14], y: [47, 50] }, "n4", { fill: "n4" }); g.line([10, 47], [13, 47], "n5"); g.line([14, 48], [14, 50], "n3"); g.line([10, 50], [13, 50], "n3"); g.line([11, 48], [13, 48], "n1"); g.line([11, 49], [12, 49], "n1"); },};const coneHalf = (y) => (y >= 52 ? 6 : 2 + Math.floor((y - 44) / 2));const SKULL = { name: "skull", ramp: "b", box: { x: [23, 35], y: [41, 53] }, liq: 47, spark: [33, 46], level: soulLevel, inside: (x, y) => inBox(x, y, 28, 30, 41, 43) || (y >= 44 && y <= 53 && Math.abs(x - 29) <= coneHalf(y) - (y === 53 ? 1 : 0)), spec: (x, y) => y >= 45 && y <= 51 && x === 29 - coneHalf(y) + 1, hot: (x, y) => x >= 31 && y >= 51, dress: (g, t) => { lipRow(g, 27, 31, 40); // bone stopper: domed cranium, two sockets, nose, jaw with teeth — lit from the top-left g.line([28, 33], [30, 33], "n4"); g.line([27, 34], [31, 34], "n4"); g.rect({ x: [26, 32], y: [35, 37] }, "n4", { fill: "n4" }); g.rect({ x: [27, 31], y: [38, 39] }, "n4", { fill: "n4" }); g.line([28, 33], [29, 33], "n5"); g.line([27, 34], [28, 34], "n5"); g.pixel(26, 35, "n5"); g.pixel(31, 34, "n3"); g.line([32, 35], [32, 37], "n3"); g.line([31, 38], [31, 39], "n3"); const lv = soulLevel(t); const eye = lv > 0 ? "b3" : lv === 0 ? "b1" : "ink"; for (const ex of [27, 30]) g.rect({ x: [ex, ex + 1], y: [36, 37] }, eye, { fill: eye }); if (lv > 0) { g.pixel(28, 37, "b2"); g.pixel(31, 37, "b2"); } g.pixel(29, 38, "ink"); g.pixel(28, 39, "n2"); g.pixel(30, 39, "n2"); },};const JUG = { name: "jug", ramp: "r", box: { x: [40, 52], y: [41, 53] }, liq: 45, spark: [50, 43], inside: (x, y) => inBox(x, y, 44, 48, 41, 42) || (y <= 53 && inEll(x, y, 40, 52, 43, 55)), spec: (x, y) => (x === 41 && y >= 47 && y <= 50) || (x === 42 && y === 46), hot: (x, y) => x >= 46 && y >= 48 && rimNear(JUG.inside, x, y), dress: (g) => { lipRow(g, 43, 49, 40); cork(g, 44, 48, 37, 39); // glass loop handle on the right g.line([52, 45], [55, 45], "n3"); g.pixel(53, 45, "n4"); g.line([56, 46], [56, 50], "n2"); g.line([53, 51], [55, 51], "n1"); },};function drawVessel(g, V, t) { g.depth(-1, () => g.outlined("ink", () => g.part(V.name, () => { vessel(g, V, t); V.dress(g, t); })));}function tubes(g, t) { g.depth(-1, () => g.outlined("ink", () => g.part("tubes", () => { g.line([45, 20], [55, 20], "w1"); // far half of the rack's top, behind the tubes vessel(g, TUBE_A, t); vessel(g, TUBE_B, t); cork(g, 46, 49, 7, 9); cork(g, 51, 54, 9, 11); // rack: near half of the top, then the front face g.line([45, 21], [55, 21], "w3"); g.line([45, 21], [49, 21], "w4"); g.rect({ x: [45, 55], y: [22, 25] }, "w2", { fill: "w2" }); g.line([45, 22], [45, 25], "w3"); g.line([45, 25], [55, 25], "w1"); g.pixel(50, 23, "g2"); g.pixel(51, 23, "g1"); // brass pin })));}// ---- animated touches ---------------------------------------------------------------------------------function bubbles(g, t) { g.depth(-2, () => g.part("bubbles", () => { for (const [x, ph] of [[18, 0], [21, 0.37], [23, 0.7]]) { const p = (t * 2 + ph) % 1; const y = 24 - Math.floor(p * 7); // 24 -> 18, the liquid line const bx = x + (y % 3 === 0 ? 1 : 0) - (x === 23 && y >= 23 ? 1 : 0); if (y > 20) g.pixel(bx, y, "e3"); // small bubble leaving the bottom else if (y > 18) { g.rect({ x: [bx, bx + 1], y: [y, y + 1] }, "e2", { fill: "e2" }); g.pixel(bx, y, "e3"); } else { g.pixel(bx, 17, "e3"); g.pixel(bx - 1, 16, "e2"); g.pixel(bx + 2, 16, "e2"); } // pop } })); g.detached("vapor", () => { for (let i = 0; i < 2; i++) { const p = (t * 2 + i / 2) % 1; const x = 20 + Math.round(Math.sin(TAU * (p * 0.8 + i * 0.5)) * 1.5); const y = 5 - Math.round(p * 5); if (p < 0.4) { g.pixel(x, y, "e3"); g.pixel(x + 1, y, "e2"); } else if (p < 0.75) g.pixel(x, y, "e2"); else if (chk(x, frameOf(t))) g.pixel(x, y, "e1"); } });}function glints(g, t) { g.depth(-2, () => g.detached("glint", () => { for (const V of [VIAL, JAR, FLASK, SKULL, HEART, JUG, TUBES]) { const k = glintStep(V.name, t); if (k > 0) sparkle(g, V.spark[0], V.spark[1], k); } }));}function soulLight(g, t) { const lv = soulLevel(t); // soul sparks drift up through the liquid while it is lit g.depth(-2, () => g.part("sparks", () => { if (lv < 0) return; for (const [x, ph] of [[27, 0], [30, 0.5]]) { const p = (t * 2 + ph) % 1; const y = 52 - Math.floor(p * 5); // 52 -> 48, just under the liquid line g.pixel(x + (y % 2), y, lv > 0 ? "n5" : "b3"); } })); // light pooling on the plank top either side of the base if (lv < 1) return; g.depth(-2, () => g.part("spill", () => { g.line([20, 53], [22, 53], "b1"); g.pixel(22, 54, "b2"); g.pixel(21, 54, "b1"); g.line([36, 53], [38, 53], "b1"); g.pixel(36, 54, "b2"); g.pixel(37, 54, "b1"); }));}// ---- the shelf ----------------------------------------------------------------------------------------function sideBoard(g, x0, name) { g.part(name, () => { g.rect({ x: [x0, x0 + 2], y: [7, 60] }, "w2", { fill: "w2" }); g.line([x0, 7], [x0, 60], "w3"); g.line([x0 + 2, 7], [x0 + 2, 60], "w1"); g.pixel(x0 + 1, 61, "w1"); // tapered foot for (const y of [15, 16, 40, 41, 42]) g.pixel(x0 + 1, y, "w1"); // grain // brass finial g.pixel(x0 + 1, 3, "g2"); g.pixel(x0, 4, "g3"); g.pixel(x0 + 1, 4, "g2"); g.pixel(x0 + 2, 4, "g1"); g.pixel(x0 + 1, 5, "g1"); g.line([x0, 6], [x0 + 2, 6], "g0"); // brass nail heads where the planks are housed g.pixel(x0 + 1, 28, "g2"); g.pixel(x0 + 1, 56, "g2"); });}function plank(g, y0, name, casts) { g.part(name, () => { g.line([5, y0], [58, y0], "w2"); // back strip, shaded by the wall g.rect({ x: [5, 58], y: [y0 + 1, y0 + 2] }, "w3", { fill: "w3" }); // top face g.line([5, y0 + 2], [22, y0 + 2], "w4"); // front edge catches the key light g.rect({ x: [5, 58], y: [y0 + 3, y0 + 5] }, "w2", { fill: "w2" }); // front face g.line([5, y0 + 5], [58, y0 + 5], "w1"); for (const [a, b, r] of [[9, 13, 4], [21, 24, 3], [34, 39, 4], [48, 51, 3]]) g.line([a, y0 + r], [b, y0 + r], "w1"); g.pixel(29, y0 + 4, "w0"); g.pixel(28, y0 + 4, "w1"); g.pixel(30, y0 + 4, "w1"); // knot // each vessel casts a short shadow back-right across the plank top (vessels occlude the rest) for (const xr of casts) { g.line([xr + 1, y0 + 1], [xr + 3, y0 + 1], "w2"); g.pixel(xr + 1, y0 + 2, "w2"); } });}function gusset(g, xw, y, dir, name) { // brass bracket under a plank, fixed to the side board: a right triangle with a lit top edge and a screw g.part(name, () => { for (let i = 0; i < 4; i++) g.line([xw, y + i], [xw + dir * (3 - i), y + i], "g1"); g.line([xw, y], [xw + dir * 3, y], "g2"); if (dir > 0) { g.line([xw, y], [xw, y + 3], "g2"); g.pixel(xw, y, "g3"); } for (let i = 0; i < 4; i++) g.pixel(xw + dir * (3 - i), y + i, dir > 0 ? "g0" : "g1"); g.pixel(xw + dir, y + 1, "g0"); });}function wallShadow(g) { // the planks cast a thin line of shadow on the wall (drawn behind everything: lands only on bare wall) g.depth(1, () => g.part("wallShadow", () => { for (const y of [31, 59]) for (let x = 6; x <= 59; x++) if (chk(x, y)) g.pixel(x, y, "ink"); }));}const Shelf = sprite(pal, (g, t) => { for (const V of [VIAL, FLASK, HEART, JAR, SKULL, JUG]) drawVessel(g, V, t); tubes(g, t); bubbles(g, t); glints(g, t); g.outlined("ink", () => { sideBoard(g, 1, "boardL"); sideBoard(g, 60, "boardR"); plank(g, 24, "plankTop", [12, 23, 37, 56]); plank(g, 52, "plankBottom", [17, 35, 52]); gusset(g, 5, 30, 1, "gussetTL"); gusset(g, 58, 30, -1, "gussetTR"); gusset(g, 5, 58, 1, "gussetBL"); gusset(g, 58, 58, -1, "gussetBR"); }); soulLight(g, t); wallShadow(g);});submit(Shelf, { size: [64, 64], origin: "top-left", kind: "icon", scale: 1, animate: { fps: 10, duration: 1.6, loop: true }, anchor: { x: 32, y: 61 }, variants: [{ name: "idle" }],});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.