Guide — how this site was made
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
#FAF7F2#232323#002FA7#4F74E3#A9BFFF#8B867CThe 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.