Generative · the showcase
Ink, Ice, and Ten Thousand Birds.
Our two TypeScript libraries, showing off live on the page — no videos, no screenshots, every frame computed as you read. A Qing-dynasty cracked-ice window built by actual recursion, casting a real shadow. Ten thousand birds over reflecting water in one draw call, zero CPU per frame. A 4,096-segment dragon curve drawing itself in walking colour. And the universal trick: one recipe running twice, GPU beside CPU, each canvas telling you honestly which machine it landed on. The code that made each scene sits beside it.
Every frame on this page is being computed as you read it. Nothing here is a video, a GIF, or a screenshot — each canvas is Generative Engine, our TypeScript WebGL2 engine, running live in your browser; its chart-first sibling Generative Charts shows off live on its own page. Zero dependencies; the browser is the only runtime. The code beside each scene is the code that made it.
The ice-ray window
Qing-dynasty window makers had a pattern called 冰裂纹 — the cracked-ice lattice. The rule that makes it is startlingly modern: take a pane, split it with one bar, then split the biggest remaining pane, and again, until the window is full of cells no two of which are alike. That is a recursive algorithm, practised in rosewood three centuries before anyone wrote one down in code. Here it is as actual recursion — sixty splits of a moon gate, seeded so this window is this page’s window, each crack a screen-space ribbon with a real pixel width, the whole lattice casting a true shadow onto the floor behind it.
// the craftsman's rule: always split the largest pane
const polys = [moonGate(26, 196)]; // one 26-gon, radius 196
for (let k = 0; k < 60; k++) {
polys.sort((p, q) => area(q) - area(p));
const pane = polys.shift(); // the biggest pane
const [A, B] = randomChord(pane); // one bar across it
chords.push([A, B]);
polys.push(...split(pane, A, B)); // two panes where one was
}
// sixty chords -> sixty ribbons, depth-tested, shadow-casting
for (const [a, b] of chords)
w.ribbon([v3(a.x, a.y, 0), v3(b.x, b.y, 0)],
{ hue: 40, sat: 26, light: 62, alpha: 0.95, width: 2.6, group: "window" });Ten thousand birds
This is the scene we point at when someone asks what “super complex but simple to perceive” means. Three thousand birds on screen — few enough that you can read the wingbeats and watch the lobes split and merge — each with its own orbit, its own phase, and the whole murmuration is one draw call; the same call carries ten thousand by changing a single number. The flock never touches the CPU: every position is evaluated in the vertex stage from time alone. Under it, real water — the scene renders a second time through the reflected camera, birds included, and a wave-displaced surface samples that reflection with a fresnel blend. Your eye reads it in half a second. Your frame budget barely notices it.
// a bird is a body and two wings — grown from code, no model files
const bird = mergeMesh(
transformMesh(icosphere(2.4, 0), mCompose({ scale: v3(1, 0.7, 1.6) })),
transformMesh(plane(13, 5, 1), mCompose({ translate: v3(-7, 1.2, 0) })),
transformMesh(plane(13, 5, 1), mCompose({ translate: v3(7, 1.2, 0) })),
);
// a murmuration is ONE instanced draw — the instance transforms are the
// shared flock frame: place it high over the water and let it wander
w.meshInstanced(bird, Array.from({ length: 10000 },
() => ({ translate: v3(0, 260, 0) })),
{ hue: 206, sat: 26, light: 66, flight: true });
// carve a basin, get a lake — reflections include the flock
w.water = { ...w.water, on: true, level: 2, hue: 220, amp: 2.6 };The dragon draws itself
Fold a strip of paper in half twelve times, unfold every crease to a right angle, and you get the Heighway dragon — 4,096 segments that never cross, tiling the plane with copies of themselves. Complexity from one fold rule. Here the curve unfurls in front of you as a per-frame ribbon, the hue walking the spectrum as it goes, a glow riding the pen tip. When it finishes, it waits, and begins again.
// twelve folds -> 4,095 turns -> 4,097 points (computed once)
for (let i = 0; i < 12; i++)
turns.push(1, ...[...turns].reverse().map((t) => -t));
// the reveal: draw the first k points, k grows with time
w.grow((t) => {
const reveal = Math.min(pts.length, 8 + Math.floor((t % 70) * 115));
for (let c = 0; c * 84 < reveal - 1; c++) // hue walks chunk by chunk
w.drawRibbon(pts.slice(c * 84, Math.min(reveal, c * 84 + 85)),
{ hue: (c * 11 + t * 10) % 360, sat: 64, light: 58, alpha: 0.92, width: 2.4 });
w.drawGlow(pts[reveal - 1], (t * 40) % 360, 22, 0.9); // the pen tip
});One recipe, night and ink
The universal claim, demonstrated rather than made. Below, one recipe function — nine breathing ridge lines and a wandering sun — is called twice, verbatim, on two engine worlds. The left renders it as night: HDR, bloom, the dark the light needs. The right is the ink edition — theme: "light" — the identical recipe on paper. Same verbs, same code, a different sky. And the family’s other universality runs live one page over: the charts sibling drives the same verb surface on a GPU renderer and a CPU canvas renderer that runs anywhere, picks one for you (renderer: "auto"), and tells you honestly which you got via w.mode.
theme: "light" renders the identical recipe on paper; lightness inverts, strokes blend normally. Not two scenes — one recipe, two skies.const recipe = (w, t) => { // nine breathing ridge lines
for (let r = -4; r <= 4; r++) {
let prev = null;
for (let i = 0; i <= 88; i++) { // plain math, segment by segment
const p = ridgePoint(r, i, t);
if (prev) w.drawLine(prev, p, `hsl(${210 + r * 14} 82% 54%)`, 0.85, 2);
prev = p;
}
}
w.drawGlow(v3(Math.cos(t * 0.4) * 100, 108, 0), 285, 26, 0.9); // the sun
};
const night = createWorld({ canvas: left, theme: "dark" });
const paper = createWorld({ canvas: right, theme: "light" }); // the ink edition
night.grow((t) => recipe(night, t));
paper.grow((t) => recipe(paper, t)); // ONE recipe, two skiesTake them home
Two packages, one family. The charts library is small, stable, chart-first — one JSON-shaped recipe in, an animated chart out, GPU or CPU chosen for you. The engine is where the family stretches: HDR bloom, SSAO, real shadow maps, morphs, skeletal figures, water, and the ten-thousand-bird trick above. Chart recipes port forward by changing one import.
npm install @chromatic-coherence/generative-charts
npm install @chromatic-coherence/generative-engineAPI references live at charts and engine; the product pages hold more live scenes. Both are source-available and free for individuals, charities and small teams; one flat £100 covers any larger organisation, forever. And everything you saw — the lattice, the flock, the dragon, the twin ridges — ships in the packages you just installed. Go make something that breathes.