概要
Tessera は、宣言的なプログラムをドット絵(ピクセルアート)にコンパイルする TypeScript 埋め込み DSL です。純粋で決定論的なコンパイラであり、ツールチェーンに AI 画像生成は一切含みません。想定する「作者」は生成 AI そのもので、モデルが高水準で合成的なコード(LLM が得意とする領域)を書き、コンパイラがそれをピクセルへと変換します(生の座標計算という LLM が苦手な部分をコンパイラが肩代わりする)。中核となる考え方は 3 つです。第一に「型としてのパレット」── パレットは宣言で定義し、すべての色引数はそのキーに制約されるため、パレット外の色は実行時の事故ではなくコンパイルエラーになります。第二に「第一級の時間」── スプライトは (graphics, t, …) の純関数で t は 0..1 に正規化され、コンパイラが t を走査してアニメフレームを生成するので時間的一貫性が構造的に保証されます。第三に「決定論」── 同じプログラムと同じシードからは、バイト単位で同一の画像が得られます。
Overview
Tessera is a TypeScript embedded DSL that compiles declarative programs into pixel art. It is a pure, deterministic compiler, and the toolchain contains no AI image generation. The intended author is a generative model: the model writes high-level, compositional code (what LLMs are good at) and the compiler turns it into pixels (taking over the raw coordinate math that LLMs are bad at). Three ideas sit at the core. First, palette-as-type: palettes are declared, every color argument is constrained to a palette key, so an off-palette color is a compile error rather than a runtime accident. Second, time as a first-class citizen: a sprite is a pure function of (graphics, t, …) with t normalized to 0..1, and the compiler sweeps t to produce animation frames, so temporal consistency is structural. Third, determinism: the same program plus the same seed yields a byte-for-byte identical image.
動機・経緯
系譜は 3D 魔法陣アニメジェネレータ(magic-circle-generator)です。あちらで磨いた「決定論的コンパイラ+シード付き乱数+レイヤー合成+時間軸」という設計を、ドット絵という別ドメインに持ち込んだのが出発点でした。生成 AI に画像そのものを描かせるのではなく、「絵を組み立てるプログラム」を書かせれば、構造・一貫性・再現性をコード側で保証できるのではないか── という発想から始まっています。
Tessera descends from the 3D Magic Circle Generator. The design honed there — a deterministic compiler, seeded RNG, layer compositing, and a time axis — was carried over into a new domain: pixel art. The starting hypothesis was that instead of having a generative model draw the image directly, you have it write a program that assembles the image, so that structure, consistency, and reproducibility can be guaranteed in code.
技術的なポイント
肝は言語そのものより API 設計です。AI が書きやすい高水準コンビネータ── mirrorX/radial/repeat/distribute、関係を宣言して座標はコンパイラが解く box/anchor/attach、前進運動学で手脚が外れない joint/pose ── を揃えています。プロトタイプ継承により、1 つのベーススプライトから extend(アニメ)と repalette(色だけ差し替えたバリアント)を生やせ、part/override で名前付きパーツだけを差し替えられます。PNG/GIF エンコーダは Node の zlib だけを使う純 TypeScript 実装(GIF89a+LZW を自前実装)で、ネイティブ依存はゼロ。さらに LLM オーサリングの閉ループ── コード生成 → node:vm サンドボックス実行 → 構造化エラー修復(パレット外の色を含む)→ レンダ画像のビジョン講評 → 改訂 ── と、画像に加えて連結成分・パーツ bbox・はみ出し・ASCII グリフマップを並べる二系統のレンダレポートを実装しています。
The heart of the project is API design rather than the language itself. It offers high-level combinators that models find easy to write: mirrorX/radial/repeat/distribute; relational layout where you declare connections and the compiler solves coordinates (box/anchor/attach); and forward-kinematics joint/pose so limbs stay attached. Prototype inheritance lets a single base sprite grow into extend (animation) and repalette (a color-swapped variant), with part/override replacing just one named part. The PNG/GIF encoders are pure TypeScript using only Node's zlib (a hand-written GIF89a + LZW), with zero native dependencies. On top of that sits an LLM authoring loop — generate code → run in a node:vm sandbox → structured error repair (including off-palette colors) → vision critique of the rendered image → revise — plus a dual-channel render report that pairs the image with connected components, part bounding boxes, overflow, and an ASCII glyph map.
苦労した点・学んだこと
「AI に書かせる」前提だからこそ、API は人間にとっての書きやすさよりも、モデルが破綻しにくい制約の置き方を優先する必要がありました。型としてのパレットはその代表例で、色のミスをコンパイル時に潰すことで講評ループの反復回数そのものを減らせます。サンドボックスは node:vm を使っていますが、これは隔離の便宜であって堅牢な境界ではない(コードは自分のモデルが自分のプロンプトから生成したものだ)という前提を明文化し、信頼できない入力では別プロセスやコンテナで実行すべきと割り切っています。制約をどこに置くかで AI の出力品質が大きく変わる、というのが一番の学びでした。
Because the whole premise is that a model does the writing, the API had to prioritize constraints that keep a model from going off the rails over what feels natural to a human. Palette-as-type is the clearest example: catching color mistakes at compile time directly reduces how many times the critique loop has to iterate. The sandbox uses node:vm, but the docs are explicit that this is a convenience for isolation, not a hard boundary — the code is generated by your own model from your own prompt — so for untrusted input it should run in a separate process or container. The biggest lesson was how much the placement of constraints shapes the quality of what the model produces.
現状とこれから
静的パイプライン(型パレット・プリミティブ・mirror/radial・ディザ・アウトライン)からアニメ GIF、プロトタイプ継承、再帰+手続きパターン+方向性陰影、LLM オーサリングループ+レンダレポートまでが完了済みです。さらにその上に「自律コンテンツ工場」を載せ、(program, seed) を再現可能な遺伝子(genome)として扱い、無料の決定論ゲートでふるいにかけ → ニッチごとの最良を MAP-Elites アーカイブに保持し → コードレベルの変異で進化させ → キュレーションする、という生成パイプラインを GitHub Actions で定期・無人運用しています。ライセンスは専有/商用で、現時点では一般配布していません(一方で、Tessera を使ってあなたが生成したコンテンツ自体は、自由に利用・配布・販売できます)。
Done so far: the static pipeline (typed palettes, primitives, mirror/radial, dithering, outlining), animated GIF, prototype inheritance, recursion plus procedural patterns plus directional shading, and the LLM authoring loop with render reports. On top of that runs an autonomous content factory: it treats (program, seed) as a reproducible genome, sieves renders through a free deterministic gate, keeps the best per niche in a MAP-Elites archive, evolves winners through code-level mutation, and curates — a generation pipeline run unattended on a schedule via GitHub Actions. The license is proprietary/commercial and the tool is not generally distributed at this time (though content you generate with Tessera is yours to use, distribute, and sell freely).