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.

Title plate — 'Ink, Ice, and Ten Thousand Birds': a cracked-ice lattice circle beside a swirl of birds.
Title plate — 'Ink, Ice, and Ten Thousand Birds': a cracked-ice lattice circle beside a swirl of birds.

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.

冰裂纹 — sixty recursive splits of a moon gate, seeded; every crack a depth-tested ribbon, the lattice casting a real shadow. Drag to orbit.
// 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.

Three thousand birds low over real water, one instanced draw call, zero CPU per frame — the same call carries ten thousand. Drag to orbit.
// 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.

The Heighway dragon — twelve folds, 4,096 segments — drawing itself as a per-frame ribbon, hue walking, a glow riding the pen tip.
// 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 editiontheme: "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.

The same recipe function, called twice. Left: the night — HDR, bloom, the dark the light show needs. Right: the ink editiontheme: "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 skies

Take 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-engine

API 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.

← All investigations