Tessarune
Graveyard & Chapel · crypt_door.js

霊廟の扉

64×64 · JavaScript 386 行

64×64 の床置きの小物で、正面を向いています。翼のある髑髏を彫った破風、段のある台座に立つ 2 本の柱、紫の紋章を刻んだ要石を備えた小さな霊廟の石の正面に、鉄帯で補強した板の重い両開き扉と輪の取っ手が付いています。closed(12 コマ)は敷居に沿って白い霧が這い、紋章が脈打ちます。open(10 コマの一回再生)は封印が光り、扉が内側へ開いて行き過ぎてから止まり、まぐさから砂が落ち、祭壇の魂火が浮かび上がります。open-idle(12 コマ)は魂火が息づき、霧が段の上へあふれます。

霊廟の扉 — 描画結果6×
このプログラムで描画(変種「open」) · 64×64 · 10 コマ
Graveyard & Chapel動くスプライト素材: Graveyard & Chapel(itch.io)Vol.4・小物 12 点・33 アニメ

冒頭のコメント。これが指示書です。サイズ、視点、光の向き、すべての変種が書かれています。

crypt_door.js · 1–9
// Crypt door — 64x64 floor prop, top-down 3/4, key light top-left. Camera-facing.// The stone facade of a small mausoleum: a pediment with a carved winged skull under an orb finial, a dentilled// cornice, two columns on a stepped plinth, an ashlar wall with a moulded door surround, and a keystone carrying a// violet sigil. Heavy double doors of iron-banded planks with ring pulls.// Variants://   closed     12f loop  pale fog creeps out along the doorsill, the sigil on the keystone pulses//   open       10f once  the seal flares, the doors jolt and swing inward (overshoot, settle), grit falls from the//                        lintel; the dark tomb and a soulfire on an altar fade in; ends on open-idle frame 0//   open-idle  12f loop  doors open, the soulfire breathes on its altar, fog spills out over the steps

最後の数行。submit でキャンバスの大きさと、描画エンジンが書き出す変種の一覧を指定します。

crypt_door.js · 376–386
submit(Crypt, {  size: [W, H], origin: "top-left", kind: "icon", scale: 1,  animate: { fps: 10, duration: N_IDLE / 10, loop: true },  anchor: { x: 32, y: GROUND },  args: ["closed"],  variants: [    { name: "closed", args: ["closed"] },    { name: "open", args: ["open"], animate: { fps: 10, duration: N_OPEN / 10, loop: false } },    { name: "open-idle", args: ["open-idle"] },  ],});
プログラム全体を表示 (386 行)
crypt_door.js · 386 行
// Crypt door — 64x64 floor prop, top-down 3/4, key light top-left. Camera-facing.// The stone facade of a small mausoleum: a pediment with a carved winged skull under an orb finial, a dentilled// cornice, two columns on a stepped plinth, an ashlar wall with a moulded door surround, and a keystone carrying a// violet sigil. Heavy double doors of iron-banded planks with ring pulls.// Variants://   closed     12f loop  pale fog creeps out along the doorsill, the sigil on the keystone pulses//   open       10f once  the seal flares, the doors jolt and swing inward (overshoot, settle), grit falls from the//                        lintel; the dark tomb and a soulfire on an altar fade in; ends on open-idle frame 0//   open-idle  12f loop  doors open, the soulfire breathes on its altar, fog spills out over the stepsconst W = 64, H = 64, CX = 31.5, GROUND = 61;const DX0 = 22, DX1 = 41, DY0 = 31, DY1 = 53;        // doorway (inclusive)const LEAF = 10, RISE = 4;                           // leaf width, how far a fully turned free edge rises (depth)const SLOPE = 2.1, APEX = 4.5;                       // pediment rakeconst N_IDLE = 12, N_OPEN = 10, REST = 80;const OPEN_TH = [0, 3, 9, 21, 37, 53, 67, 79, 85, REST];const WOOD = ["w0", "w1", "w2", "w3", "w4"], STONE = ["n0", "n1", "n2", "n3", "n4"];const clampI = (i, n) => Math.max(0, Math.min(n - 1, i));function dim(key, k) {  // step a key down its own ramp  if (!k) return key;  const r = key[0] === "w" ? WOOD : key[0] === "n" ? STONE : null;  if (!r) return key;  const i = r.indexOf(key);  return i < 0 ? key : (i - k < 0 ? "ink" : r[i - k]);}const BAYER = [[0, 8, 2, 10], [12, 4, 14, 6], [3, 11, 1, 9], [15, 7, 13, 5]];const thr = (x, y) => (BAYER[y & 3][x & 3] + 0.5) / 16;// ---- facade regions ----------------------------------------------------------------------------------------const pedEdge = (x) => APEX + Math.abs(x + 0.5 - 32) / SLOPE;      // top edge of the raking cornicefunction region(x, y) {  if (y <= 5 && x >= 29 && x <= 34) return "finial";  if (y >= 5 && y <= 16 && y >= pedEdge(x) - 0.2) return "pediment";  if (y >= 17 && y <= 21 && x >= 5 && x <= 58) return y >= 20 && (x < 7 || x > 56) ? null : "entablature";  if (y >= 54 && y <= 57 && x >= 9 && x <= 54) return "steps";  if (y >= 58 && y <= 60 && x >= 5 && x <= 58) return "steps";  const inCol = (a) => (y >= 22 && y <= 24 && x >= a - 2 && x <= a + 6) || (y >= 50 && y <= 53 && x >= a - 2 && x <= a + 6) ||    (y >= 25 && y <= 49 && x >= a && x <= a + 4);  if (inCol(10) || inCol(49)) return "columns";  if (y >= 22 && y <= 53 && x >= 15 && x <= 48) {    if (x >= DX0 && x <= DX1 && y >= DY0) return null;             // doorway: interior / leaves    return "wall";  }  return null;}function finialTone(x, y) {  if (y === 5) return x === 30 || x === 33 ? null : x === 31 ? "n3" : "n2";  const i = litIndex(x, y, 32, 2.7, 2.6, 2.6, 4, 0.1);  if (i < 0) return null;  return x === 31 && y === 1 ? "n5" : STONE[i + 1];}function pedTone(x, y) {  const dv = y + 0.5 - pedEdge(x), left = x < 32;  if (dv < 1.1) return left ? "n4" : "n3";                          // top of the raking cornice  if (dv < 2.4) return left ? "n3" : "n2";                          // its front fascia  if (dv < 3.3) return "n0";                                        // shadow cast into the recessed tympanum  if (y === 16) return "n0";  return (x + y * 3) % 17 === 0 ? "n0" : "n1";                      // tympanum, a few pits}function entTone(x, y) {  if (y === 17) return x < 8 || x > 55 ? "n4" : x < 32 ? "n4" : "n3";  if (y === 18) return x === 5 ? "n4" : x === 58 ? "n2" : "n3";  if (y === 19) return x < 7 || x > 56 ? "n1" : (x & 1) ? "n1" : "n3"; // dentils  if (y === 20) return x === 7 ? "n4" : "n3";  return x === 56 ? "n1" : "n2";}function colTone(x, y) {  const a = x < 32 ? 10 : 49, u = x - a;  if (y <= 24) {                                                    // capital: abacus + echinus    if (y === 22) return u === -2 ? "n4" : u === 6 ? "n2" : "n3";    if (y === 23) return u <= 0 ? "n4" : u <= 3 ? "n3" : "n2";    return u < -1 || u > 5 ? null : u <= 1 ? "n2" : "n1";  }  if (y >= 50) {                                                    // base: torus + plinth    if (y === 50) return u < -1 || u > 5 ? null : u <= 1 ? "n4" : u <= 3 ? "n3" : "n2";    if (y === 51) return u <= -1 ? "n3" : u <= 3 ? "n2" : "n1";    if (y === 52) return u === -2 ? "n4" : u === 6 ? "n1" : "n3";    return u === 6 ? "n0" : "n2";  }  let k = ["n3", "n4", "n3", "n2", "n1"][u];  if (y === 25) k = dim(k, 1);                                      // shadow under the capital  if (u === 2 && (y % 6 === 3)) k = "n2";                           // weathered flute marks  return k;}function wallTone(x, y) {  if (y === 22) return "n0";                                        // deep shadow under the frieze  if (y === 23) return "n1";  if (x === 15 || (x === 16 && y > 24)) return "n1";                // the left column's shadow  // door surround: jambs and lintel with a keystone  if (y >= 25 && y <= 30 && x >= 29 && x <= 34) {    if (y === 25) return x === 29 ? "n4" : x === 34 ? "n2" : "n4";    return x === 29 ? "n4" : x === 34 ? "n1" : "n3";  }  if (y >= 27 && y <= 30 && x >= 19 && x <= 44) {    if (y === 27) return x === 44 ? "n2" : "n4";    if (y === 30) return x >= DX0 && x <= DX1 ? "n1" : "n2";    return x === 19 ? "n4" : x === 44 ? "n1" : "n3";  }  if (y >= 31 && (x === 20 || x === 21 || x === 42 || x === 43)) {    if (x === 20 || x === 42) return "n4";    return "n2";  }  if (y >= 31 && (x === 19 || x === 44)) return x === 19 ? "n2" : "n0";  // surround's shadowed return  // ashlar: 4-row courses, staggered joints  const r = Math.floor((y - 24) / 4), k = (y - 24) % 4;  if (k === 3) return "n1";  const bx = x + (r & 1) * 3;  if (bx % 7 === 0) return "n1";  const dark = hash(Math.floor(bx / 7), r) < 0.3;  if (k === 0) return dark ? "n2" : "n3";  return dark ? "n1" : "n2";}function stepTone(x, y) {  if (y === 54) return x === 9 ? "n3" : (x >= DX0 && x <= DX1) ? "n3" : "n2";   // back of the upper tread, sill  if (y === 55) return x === 54 ? "n3" : "n4";  if (y === 56) return x === 9 ? "n3" : "n2";  if (y === 57) return "n1";  if (y === 58) return x < 9 || x > 54 ? (x === 5 ? "n4" : "n4") : "n3";  if (y === 59) return x === 5 ? "n3" : x === 58 ? "n1" : "n2";  return "n1";}const TONES = { finial: finialTone, pediment: pedTone, entablature: entTone, columns: colTone, wall: wallTone, steps: stepTone };function facade(g) {  for (const name of ["finial", "pediment", "entablature", "wall", "columns", "steps"])    g.part(name, () => g.field({ x: [0, W - 1], y: [0, H - 1] }, (x, y) => (region(x, y) === name ? TONES[name](x, y) : null)));}function carvings(g) {  // winged skull in the tympanum (relief: lit upper-left edges, shadowed lower-right)  g.part("skull", () => {    const S = [      "..####..",      ".######.",      "#XX##XX#",      "#XX##XX#",      ".##NN##.",      "..TTTT..",    ];    S.forEach((row, j) => [...row].forEach((c, i) => {      const x = 28 + i, y = 10 + j;      if (c === ".") return;      let k;      if (c === "X") k = (j === 2 && (i === 1 || i === 5)) ? "n0" : "ink";      else if (c === "N") k = i === 3 ? "n1" : "n0";      else if (c === "T") k = (i & 1) ? "n2" : "n4";      else k = (i <= 2 && j <= 1) || i === 0 ? "n5" : i >= 6 || j >= 4 ? "n3" : "n4";      g.pixel(x, y, k);    }));    for (const [x, y] of [[36, 12], [36, 13], [35, 14], [34, 15], [34, 16], [33, 16], [32, 16], [31, 16], [30, 16]]) g.pixel(x, y, "n0");    const wing = (s) => {      // a swept wing of three feathers: lit top edge, relief face, hard shadow underneath      const sx = (d) => s < 0 ? 27 - d : 36 + d;      const rows = [[12, 0, 3], [13, 0, 6], [14, 0, 8], [15, 1, 7]];      for (const [y, d0, d1] of rows) for (let d = d0; d <= d1; d++) {        let k = "n3";        if (y === 12 || (y === 13 && d >= 4) || (y === 14 && d >= 7)) k = s < 0 ? "n4" : "n3";        if (y === 15) k = d === 3 || d === 6 ? "n0" : "n1";        if (y === 14 && (d === 3 || d === 6) ) k = "n2";        g.pixel(sx(d), y, k);      }      g.pixel(sx(8), 15, "n0");    };    wing(-1); wing(1);  });  g.part("weathering", () => {    // rain streaks from the cornice, a crack, moss at the feet    for (const [x, y0, n] of [[17, 24, 5], [46, 24, 3], [24, 24, 2]]) for (let i = 0; i < n; i++) g.pixel(x, y0 + i, i === n - 1 ? "n2" : "n1");    g.pixel(52, 30, "n1"); g.pixel(51, 31, "n1"); g.pixel(51, 32, "n2"); g.pixel(52, 33, "n1");  // crack in the right shaft    g.pixel(45, 14, "n2");                                             // pits in the tympanum    for (const [x, y, k] of [[8, 53, "e1"], [9, 53, "e2"], [10, 53, "e1"], [8, 52, "e1"], [47, 53, "e1"], [48, 53, "e2"],      [9, 55, "e1"], [10, 54, "e2"], [11, 54, "e1"], [53, 56, "e1"], [54, 56, "e1"], [5, 59, "e1"], [6, 58, "e2"], [7, 58, "e1"],      [57, 59, "e1"], [58, 59, "e1"], [16, 51, "e1"], [16, 52, "e2"], [15, 53, "e1"]]) g.pixel(x, y, k);    g.pixel(40, 59, "n1"); g.pixel(41, 59, "n0"); g.pixel(19, 56, "n1");                          // chips in the steps  });}function sigil(g, pulse) {  // a diamond rune cut into the keystone; the whole face tints violet at the peak  g.part("sigil", () => {    if (pulse > 0.75) for (const [x, y] of [[30, 26], [33, 26], [30, 30], [33, 30], [31, 26], [32, 30]]) g.pixel(x, y, "v0");    const rim = pulse > 0.6 ? "v2" : "v1", core = pulse > 0.75 ? "v3" : pulse > 0.35 ? "v2" : "v1";    g.pixel(31, 27, rim); g.pixel(32, 27, rim);    g.pixel(30, 28, rim); g.pixel(33, 28, rim);    g.pixel(31, 28, core); g.pixel(32, 28, core);    g.pixel(31, 29, rim); g.pixel(32, 29, rim);    if (pulse < 0.3) { g.pixel(31, 27, "v0"); g.pixel(32, 29, "v0"); }  });}// ---- inside ---------------------------------------------------------------------------------------------------function interior(g, glow, ft, R) {  // the tomb: black depth, a floor that catches the soulfire, an altar at the back wall  const fx = 31.5, fy = 37;  g.part("interior", () => g.field({ x: [DX0, DX1], y: [DY0, DY1] }, (x, y) => {    const d = Math.hypot(x + 0.5 - fx, (y + 0.5 - fy) * 1.25);    const r = R * glow;    if (y >= 47) {                                                     // floor      const fd = Math.hypot(x + 0.5 - fx, (y - 47) * 2.2);      if (glow > 0.2 && fd < r * 0.8) return y === 47 ? "b1" : chk(x, y) ? "b0" : "n0";      if (glow > 0.2 && fd < r * 1.2) return chk(x, y) ? "n0" : "ink";      return "ink";    }    if (glow > 0.05 && d < r * 0.45) return "b0";    if (glow > 0.05 && d < r * 0.75) return "v0";    if (glow > 0.05 && d < r * 0.95) return chk(x, y) ? "v0" : "ink";    return "ink";  }));  if (glow > 0.2) g.part("altar", () => {    { const k = glow > 0.6 ? "b1" : "n1"; g.rect({ x: [28, 35], y: [42, 42] }, k, { fill: k }); }    g.rect({ x: [28, 35], y: [43, 46] }, "n0", { fill: "n0" });    g.line([28, 43], [35, 43], "b0");    g.pixel(28, 42, "n2"); g.pixel(35, 42, "n1");      });  if (glow > 0.35) g.detached("soulfire", () => {    const h = Math.round((9 + 1.5 * Math.sin(TAU * ft)) * Math.min(1, glow * 1.2));    flame(g, 32, 41, 6, h, ft, 0.2, ["b1", "b2", "b3", "n5"]);    flame(g, 29, 41, 3, Math.max(2, h - 4), ft, 0.65, ["b1", "b2", "b3", "n5"]);    // a wisp drifting up    const p = ft % 1;    if (p < 0.7) g.pixel(31 + Math.round(Math.sin(TAU * p) * 2), 33 - Math.round(p * 3), p < 0.35 ? "b3" : "b2");  });}function leaves(g, th, lit) {  // two leaves hinged at the jambs, swinging inward; the free edge rises as it recedes  const a = th * Math.PI / 180;  const w = Math.max(1, Math.round(LEAF * Math.cos(a)));  const dy = RISE * Math.sin(a);  const tex = (sx, ly, left) => {    // closed texture at screen column sx, row ly    const lx = left ? sx - DX0 : DX1 - sx;                             // 0 at the hinge    if (ly === DY1) return "w0";    // strap hinges: two rows from the hinge, ending in a spear tip near the meeting stile    for (const by of [35, 47]) {      if (ly === by - 1 && lx === 7) return "n2";      if (ly === by + 2 && lx === 7) return "n0";      if ((ly === by || ly === by + 1) && lx <= 8) {        if (lx === 8) return ly === by ? "n2" : "n0";        const top = ly === by;        if (top && (lx === 1 || lx === 4)) return "n4";        if (!top && (lx === 1 || lx === 4)) return "n0";        return top ? (left ? "n3" : "n2") : "n1";      }    }    if (lx === LEAF - 1) return left ? "w1" : "w0";                    // meeting stiles    // ring pulls on round back-plates    const rx = left ? 29 : 34, ry = 42, ddx = sx - rx, ddy = ly - ry;    if (ddy === -3 && ddx === 0) return "n3";    if (ddy === -2 && Math.abs(ddx) <= 1) return ddx < 0 ? "n3" : ddx === 0 ? "n4" : "n1";    if (ddy >= -1 && ddy <= 2 && Math.abs(ddx) <= 1) {      if (ddx === 0 && (ddy === 0 || ddy === 1)) return "w0";      if (ddx === -1) return ddy === 2 ? "n2" : "n4";      if (ddx === 1) return "n1";      return ddy === -1 ? "n4" : "n1";    }    // planks: 3px each from the left edge of the doorway    const p = (sx - DX0) % 3, pi = Math.floor((sx - DX0) / 3);    let k = p === 0 ? "w1" : p === 1 ? "w3" : "w2";    if (!left && p === 1) k = "w2";                                     // the right leaf sits a touch darker    if (p === 2 && ly % 7 === 3 && hash(pi, ly) < 0.5) k = "w1";          // a few grain marks    if (sx === 31 || sx === 32) k = "w1";    if (ly <= DY0 + 2) k = dim(k, ly === DY0 ? 2 : 1);                   // the lintel's shadow    if (ly >= DY1 - 1) k = dim(k, 1);    return k;  };  const leafDim = (left) => (left ? (th > 55 ? 2 : th > 25 ? 1 : 0) : (th > 45 ? 1 : 0));  g.part("doors", () => g.field({ x: [DX0, DX1], y: [DY0 - 1, DY1] }, (x, y) => {    for (const left of [true, false]) {      const i = left ? x - DX0 : DX1 - x;      if (i < 0 || i >= w) continue;      const sh = w > 1 ? Math.round(dy * i / (w - 1)) : 0;      const ly = y + sh;      if (y < DY0 || ly > DY1 || ly < DY0) continue;      const lx = Math.min(LEAF - 1, Math.floor((i + 0.5) * LEAF / w));      const sx = left ? DX0 + lx : DX1 - lx;      let k = dim(tex(sx, ly, left), leafDim(left));      if (th > 30 && i === w - 1) k = lit > 0.5 && (ly & 1) ? "b1" : "w0";   // free edge, rimmed by the soulfire      if (th > 30 && ly === DY1) k = "ink";      return k;    }    return null;  }));}// ---- fog ----------------------------------------------------------------------------------------------------------function puff(g, cx, cy, rx, ry, dens) {  if (dens <= 0.02) return;  for (let y = Math.floor(cy - ry); y <= Math.ceil(cy + ry); y++) for (let x = Math.floor(cx - rx); x <= Math.ceil(cx + rx); x++) {    const nx = (x + 0.5 - cx) / rx, ny = (y + 0.5 - cy) / ry, r2 = nx * nx + ny * ny;    if (r2 > 1) continue;    const v = dens * (1 - r2);    if (v > 0.5) g.pixel(x, y, "n5");    else if (v > thr(x, y) * 0.55) g.pixel(x, y, "n4");  }}function fogSill(g, t, amt) {  // low wisps squeezing out under the doors and creeping along the sill  if (amt <= 0) return;  g.detached("fog", () => {    for (const s of [-1, 1]) for (let k = 0; k < 3; k++) {      const p = (t + k / 3 + (s > 0 ? 0.17 : 0)) % 1;      const x = CX + s * (3 + p * 16), y = 54 + p * 2.5;      puff(g, x, y, 3.2 + p * 3, 1.5 + p * 0.8, amt * Math.min(1, Math.sin(Math.PI * p) * 1.2));    }    puff(g, CX, 54, 6 + Math.sin(TAU * t), 1.5, amt * (0.75 + 0.15 * Math.cos(TAU * t)));  });}function fogSpill(g, t, amt) {  // open: fog pours over the sill and rolls down the steps, spreading outwards  if (amt <= 0) return;  g.detached("fog", () => {    for (let k = 0; k < 5; k++) {      const p = (t + k / 5) % 1, s = (k & 1) ? 1 : -1;      const x = CX + s * (1 + p * 17) + (k === 2 ? -3 : 0), y = 51.5 + p * 7.5;      puff(g, x, y, 3.5 + p * 4, 1.7 + p * 1, amt * Math.min(1, Math.sin(Math.PI * p) * 1.3));    }    puff(g, CX, 53, 8 + Math.sin(TAU * t) * 0.8, 2, amt * (0.7 + 0.12 * Math.cos(TAU * t)));  });}function grit(g, f) {  if (f < 2 || f > 6) return;  g.detached("grit", () => {    for (const [x, ph] of [[25, 0], [30, 1], [36, 0], [39, 2]]) {      const y = DY0 + (f - 2 - ph) * 4;      if (y >= DY0 && y <= DY1 - 1) { g.pixel(x, y, "n3"); if (y - 1 >= DY0) g.pixel(x, y - 1, "n1"); }    }  });}// ---- states -------------------------------------------------------------------------------------------------------function stateAt(state, t) {  t = ((t % 1) + 1) % 1;  const breathe = (tt) => 0.5 + 0.5 * Math.cos(TAU * tt);  if (state === "closed") {    const f = Math.round(t * N_IDLE) % N_IDLE, tt = f / N_IDLE;    return { th: 0, glow: 0, pulse: breathe(tt), sill: 1, spill: 0, ft: tt, st: tt, R: 8, f: -1 };  }  if (state === "open-idle") {    const f = Math.round(t * N_IDLE) % N_IDLE, tt = f / N_IDLE;    return { th: REST, glow: 1, pulse: breathe(tt), sill: 0, spill: 1, ft: tt, st: tt, R: 8 + 1.2 * Math.cos(TAU * tt), f: -1 };  }  const f = Math.min(N_OPEN - 1, Math.round(t * N_OPEN));  if (f === 0) return stateAt("closed", 0);  if (f === N_OPEN - 1) return stateAt("open-idle", 0);  const tt = (((f - (N_OPEN - 1)) / N_IDLE) % 1 + 1) % 1;              // run the idle clock backwards from the end  const k = Math.min(1, f / 7);  const glow = Math.max(0, Math.min(1, (f - 2) / 6));  return { th: OPEN_TH[f], glow, pulse: 1, sill: 1 - k, spill: k, ft: tt, st: f / N_IDLE, R: 9.2, f, flare: f <= 3 };}const Crypt = sprite(pal, (g, t, state) => {  const S = stateAt(state, t);  contactShadow(g, 32, GROUND, 28, 2);  g.outlined("ink", () => {    facade(g);    interior(g, S.glow, S.ft, S.R);    leaves(g, S.th, S.glow);  });  carvings(g);  sigil(g, S.pulse);  if (S.flare) g.detached("flare", () => {    for (const [x, y] of [[28, 27], [35, 27], [31, 24], [32, 32]]) g.pixel(x, y, S.f === 2 ? "v3" : "v2");  });  if (S.glow > 0.5) g.part("spill", () => {                           // soulfire light on the sill    for (let x = 27; x <= 36; x++) if (chk(x, 54) || Math.abs(x - 31.5) < 2) g.pixel(x, 54, "b1");  });  if (S.f > 0) grit(g, S.f);  fogSill(g, S.st, S.sill);  fogSpill(g, S.ft, S.spill);});submit(Crypt, {  size: [W, H], origin: "top-left", kind: "icon", scale: 1,  animate: { fps: 10, duration: N_IDLE / 10, loop: true },  anchor: { x: 32, y: GROUND },  args: ["closed"],  variants: [    { name: "closed", args: ["closed"] },    { name: "open", args: ["open"], animate: { fps: 10, duration: N_OPEN / 10, loop: false } },    { name: "open-idle", args: ["open-idle"] },  ],});

掲載されたコードでは、ビルドスクリプトが自動付与する共通部分を省略しています。具体的には 1 行のランタイムヘッダー、共有パレット、および汎用ヘルパー関数(contactShadow、litIndex、flame、hash、chk、TAU)です。表示されている行番号は実際のファイルと一致しています。これらのプログラムは AIエージェントが生成し、描画されたコマの仕上がりを確認しながら修正を加えたものです。

エージェントに、こうしたプログラムを書かせる。

Tessarune は描画 API のリファレンス、25 冊の制作マニュアル、結果を確かめるための描画エンジンをエージェントに渡します。