Aleatory*

Composing chance,
one seed at a time.

Aleatory is site 05 of the Fable Fifteen exhibition: a generative-art studio whose portfolio regenerates on every visit. This is the working guide — the concept, the engine, the palette discipline, and what changed across three iteration passes.

1Concept & creative direction

The brief: a studio whose homepage is the portfolio — a full-bleed flow-field artwork, 10–20 k particles, seeded from crypto.getRandomValues, visibly different on every load, titled like an edition. Everything else on the page had to behave like a gallery: bone walls, restrained serif labels, one Klein-blue accent, and no motion that competes with the canvas.

The design position is a gallery inversion — the site is the frame, not the art. Where most portfolio sites decorate around static work, here the work is alive and the chrome holds still. Two ground modes make regeneration dramatic: a noir ground (deep graphite, additive luminous trails) and a bone ground (gallery white, accumulating ink). A regenerate can flip the entire page from a lit room to a drawing on paper.

2Palette & type

Gallery bone
#FAF7F2
Graphite
#232323
Klein blue
#002FA7
Cobalt (ink)
#4F74E3
Pale ink
#A9BFFF
Silverpoint
#8B867C

The particle inks are drawn from eight weighted recipes — four per ground — with weights jittered ±40% per seed, so two “Klein Drawing” compositions still differ in balance. Klein blue is the only saturated hue anywhere on the site; the UI gets it in threads (underlines, labels, one button ring) and the canvas gets it by the litre.

Erode — a display serif with a drawn, slightly eroded edge. Aa Gg №0123456789

General Sans carries the body: plain, warm, and unbothered. Aa Gg 0123456789

3Signature technique — one seed, every decision

A single uint32 from the CSPRNG is expanded (splitmix32 → sfc32) into a private random stream, and every compositional decision draws from it in order: ground, ink recipe and weights, one of six field archetypes (curl, attractors, orbit, strata, rose, vortex lattice), particle count, spawn distribution, stroke weight, trail persistence. Same seed, same work — which is what makes ?seed= a certificate of provenance.

// expand one uint32 into every decision the work will make
const rng     = rngFromSeed(seed)   // splitmix32 → sfc32, warmed
const noise   = makeNoise3D(rng)    // permutation shuffled per seed
const palette = pickPalette(rng)    // ground + weighted inks
const field   = pickField(rng)      // curl | attractors | orbit | …

// curl of a scalar noise potential ψ — divergence-free drift,
// the reason flow lines braid and never pile up
const e = 0.002
out.vx =  (ψ(x, y + e, t) - ψ(x, y - e, t)) / (2 * e)
out.vy = -(ψ(x + e, y, t) - ψ(x - e, y, t)) / (2 * e)

Rendering is Canvas 2D, tuned like a long exposure: particles live in typed arrays grouped into contiguous color ranges, so each frame is a handful of batched stroke() calls, not twenty thousand. Trails accumulate in an offscreen buffer faded with destination-out — decay toward transparency, never toward a color — and the buffer is blitted over a ground that can crossfade, which is how a regenerate melts graphite into paper. The exhale/inhale itself is four tweened numbers: dispersal (a radial force), trail alpha, a fade boost, and a 4% breath of zoom.

4Asset pipeline — zero AI images

Per the brief, no generated imagery anywhere: the engine is the only image-maker on the site. The six Editions stills and the social image were exported from the real engine at build time — a Playwright script loads /?still=1&seed=…, lets the composition develop for ~700 frames, and saves canvas.toDataURL(); stills are then optimized to WebP (≤500 KB) with the repo's sharp pipeline. The OG image is one noir seed developed at 1200×630. Fonts are self-hosted WOFF2, fetched from Fontshare's CSS API and rewritten to local URLs.

5Iteration diary

Pass 1 — structure

Most of this pass was spent calibrating chance itself. The first contact sheet of twelve seeds exposed everything at once: the noise time-scale was ~20× too fast (fields decorrelated into fuzz in under a second), attractors collapsed into radial sinks until swirl was made to dominate, and the bone palettes were too timid to read as ink. A subtler trap: a leftover height attribute on the edition images silently defeated their CSS aspect-ratio and over-cropped every print at every width. Also learned the hard way that Fontshare's CSS API and the repo's font tool disagree about protocol-relative URLs; wrote a small fetcher instead.

Pass 2 — craft

An honest entry: the builder running Pass 2 was cut off mid-pass by an API spend limit, and a second builder took over cold — re-read the engine, re-ran every check, and finished the pass. What changed: the inhale had a dead beat (a second of near-black after the plate flips to a noir ground), fixed by tripling the physics step rate while a new work condenses; the regenerate button now turns its shutter glyph through one full revolution across the breath; and the palest bone seeds — a Silverpoint rose, a Blue Hour strata — got a denser ink floor so the worst draw of the dice still reads as a drawing, not a wallpaper. Determinism, keyboard access, and reduced-motion behavior were re-verified end to end.

Pass 3 — the hostile critic

Two verdicts survived the fine-toothed comb. First: the Editions copy claimed "anyone holding the seed can resummon the work" — but the site didn't let you try. Now every print is a live certificate: click one and the page rides up while the current work exhales, and the exact archived composition re-condenses in the hero. Watching the live render converge on the print you just clicked is the whole thesis in four seconds. Second: the Studio section was the one screen with no chance in it — it now opens with a frieze, a calm laminar system drafted from your visit's seed, drawing quietly on the bench while you read about commissioning one.

6Credit

Designed & built autonomously by Claude Fable 5 (Anthropic). Site 05 of the Fable Fifteen exhibition — live at aleatory-studio.pages.dev.