the proof
One generator set. Five games.
The images below are the same alley.asset.ts, placing the same props, run five
times. Not one line differs between them. Everything you see change — the brick, the
plating, the linework weight, how crooked the fence is, whether the railing has finials — comes
from a pack manifest.
Cover the labels and you can still tell which is which. That is the test, and it is why this is not a palette swap: a pack moves the form too. Clicking a frame restyles this whole page with that pack.
| the pack declares | cyberpunk-neon | sun-bleached-western | aden-interlude |
|---|---|---|---|
| irregularity | 0.18 machined | 0.85 crude | 0.42 guild-crafted |
| bevel | 0.05 razor | 0.22 chunky | 0.14 moulded |
| facets | medium | low | high |
| ornament | 0.80 | 0.25 | 0.95 |
| heft | 0.85 slender | 1.30 stocky | 1.10 |
| light elevation | 0.35 raking | 0.85 noon | 0.50 torch |
live, in this tab
Generated live, in this tab
Not screenshots. The tiles below run the library's own generators in your browser against the pack you picked at the top of the page. Each one prints the exact call that made it — if the call ever stopped producing the picture, this page would be visibly wrong in public.
the mechanism
How it works
A generator may not contain a colour, a line weight, a tone pitch or a world scale. It names a role and the pack answers. That single indirection is the whole design.
ctx.palette.get('#3a2f28')
s.stroke(colour, 3)
mesh.chamferBox(w, h, d, 0.02)
segments = 12
ctx.palette.get('surfaceMid')
s.stroke(colour, style.outline('prop'))
mesh.chamferBox(w, h, d, style.form.bevel * w)
segments = style.sides('medium')
Same generator, different manifest, different asset — and the gates run against every pack that can host it, not just the one it was written for.
the measurement
What a new style costs
The third pack was built from one sentence — "a Lineage 2 Interlude inspired pack" — through a brief template, and it shipped zero new generators. Nine generators written earlier, by authors who had never heard of Lineage 2, rendered it unchanged.
| packs #1 and #2 | pack #3 | |
|---|---|---|
| runtime, validator, adapter | 10,276 lines | 0 — reused |
| generators | 4,682 lines / 9 files | 0 — reused |
| brief + manifest | — | 271 lines |
| generator fixes needed | — | 1 line |
the honest bit
What the validator caught
Most of the quality is here. These are real defects, each of which had already passed a careful author's own eye — and each of which was invisible to review and instant to a measurement.
| the bug | why nobody saw it |
|---|---|
| Every surface shaded toward black | A warm surface must take a cool shadow. Shading toward ink collapses a palette into one flat tone, and no lighting rig can restore a midtone the ink set never contained. All nine generators were doing it. |
| Shadows came out lighter than their flats | The fix for the above clamped HSV value — but luma weights green at 0.71, so mixing toward a teal raised luma while value stayed put. Found by a test asserting the property, not by looking. |
| The machined pack rendered softer than the handmade one | Mapping "bevel" onto a corner-cut ratio in [0, 0.5] silently inverts the axis: rounding peaks at 0.25 and vanishes at 0.5, where an n-gon becomes a rotated n-gon. |
| A gate that had never once fired | Two packs declared "props may not glow above 3%" and the coherence gate skipped every non-texture output. A silently-passing gate is worse than a missing one — an author sized their lamp for the tightest limit because they could not rely on being told. |
| Authors punished for returning measurements | The locality gate diffed a canonical byte stream where scalars are variable-length text, so one meta number gaining a digit shifted every later byte and read as 100% changed. Two authors "fixed" it by deleting the measurement. |
| A facade lost every window | Nine-fold tiling translates by a whole tile, so a band drawn wider than the canvas repaints across finished work — and because that pass's own detail is off-canvas, it erases rather than duplicates. |
where it came from
Two projects, one pipeline
Seedbank was not designed in the abstract. Its pattern library was mined from a finished game — Comic Zombies, a browser shooter with 61,300 lines, one runtime dependency and zero downloaded assets. Every texture, mesh, letterform and sound in it is generated in code, and its own gallery page proves it by running the game's modules live. PATTERNS.md quotes that repo verbatim, with file and line ranges, and calls it “the only complete, shipped, zero-downloaded-asset codebase we have.”
A pattern library written against generators that actually shipped, and a list of failures found the expensive way in a real game first: a colour crushed 13× by a second sRGB conversion, tiling that needs all three mechanisms or it seams, and a halftone that crawled until the rule became “the print is fixed, only the drawing moves.” Every one was invisible to review and instant to a measurement — which is why this project has a validator instead of a style guide.
A harder question than it had asked itself. The game hardcoded its palette, its ink weights and its tone pitch — it only ever had to be one game. Here every one of those is a pack manifest field, and that lift from constant to parameter is the whole project. Generalising a shortcut is what exposes it, so the extraction doubled as the sharpest review that art code has had.
They disagree in useful places, and the disagreements are the interesting half. That library is
welded to three.js — its generators build THREE.Meshes and touch the DOM.
Here a generator returns plain data and an adapter builds the mesh, which is what lets the same
output be validated headlessly, hashed for determinism, and exported to GLB. Those divergences
are flagged inline in PATTERNS so nobody ports the old signatures wholesale.
Each is the other's test case. The game is where a generator proves it survives a frame at 60 fps; the toolchain is where it proves it was worth writing once. Two repos, one pipeline — both public, both built the same way.
comic-zombies.vercel.app
comic-zombies source ↗
61.3k lines, 93 files, six weapons, ~369kB gzipped — one runtime dependency and zero
downloaded assets. The repo
PATTERNS.md is quoted from, line range by line range.
github.com/elberacasa/comic-zombies
try it
Try it
git clone https://github.com/elberacasa/seedbank
cd seedbank && npm install
npx seedbank # the studio: every asset, every pack, side by side
npm run validate # the five gates
npm run render # every asset x every pack, to PNG