Wiener.

No. 171 · Method & Iteration Log

Wiener — building the cone of possible futures

A live three.js ensemble of a thousand geometric-Brownian paths, fanning from one point into the widening √t envelope of uncertainty. This page shows how it was made — the model, the palette, the type, and the three iteration passes.

01 The concept

Wiener is a fictional study page for a diffusion primer — a single, quiet exhibit whose one job is to make an abstract truth visible: that a thousand random futures, each obeying the same rule, do not sprawl into chaos but into a computable shape. The audience is the mathematically curious; the tone is a laboratory ledger, not a trading desk.

The equation is geometric Brownian motion, dS = μS·dt + σS·dW — the same process behind particle diffusion and the Black–Scholes option price. We plot it in log-return space, where it becomes a pure Wiener process with drift, and its quantiles fan out exactly as ±z·σ√t. It is illustrative only — no instrument, no asset, no advice.

02 Palette

Graphite ground, cool graphite-blue for the random paths (a line/mark colour, never body text), and a single warm amber reserved for the median and the marks it earns. Body copy runs on a light ledger-ink derived to clear 4.5:1 on the ground.

Graphite
#111319
Ground
Path blue
#7C8AA6
Line / mark
Median amber
#E0A84A
Accent / mark
Ledger ink
#D8DCE4
Body — 12:1
Dim ink
#9AA3B4
Secondary — 6:1

03 Type

Newsreader carries the voice — an optical serif with a genuine italic, editorial and calm, the register of a well-set primer. JetBrains Mono holds every number, label and the SDE, so the mathematics always reads as instrument rather than prose. The one flourish is the headline's radical: an amber √t set as a mark, the whole page's idea in two glyphs.

Newsreader
display
The cone of possible futures
Newsreader
italic
No single path is a forecast. Their spread is.
JetBrains
Mono
dS = μS·dt + σS·dW   90% band ±0.90

04 Technique

The scene is three.js with an EffectComposer bloom pass. The ensemble is a single LineSegments buffer holding 1,100 paths × 130 steps — roughly 288,000 vertices in one draw call. Each path is a geometric-Brownian walk computed on the CPU; every path starts at the same origin and fans outward in a shallow depth slot, so the cone opens in three dimensions, not just two.

The signature — a thousand futures fanning into a cone — is drawn additively: overlapping threads sum to light, so the region of highest path density glows brightest along the median and thins toward the tails. That luminous falloff is the probability density, rendered for free by the blend mode. Over it, the analytic 10 / 50 / 90 quantile curves and a translucent band draw the exact ±z·σ√t envelope, and a bloomed tube lights the median. A custom vertex shader carries a per-path phase so a travelling highlight runs each thread outward — the paths visibly extending — with no synchronised front to read as a curtain.

A faint amber caliper scans the cone left to right; the ledger's band at t readout is tied to its position, so the number and the mark measure the same widening. The ensemble quietly resamples every nine seconds behind a soft crossfade — a fresh thousand futures, the same law.

Guardrails: devicePixelRatio capped at 2, resize re-fits camera and bloom, the rAF loop pauses on document.hidden with a clamped delta on resume, a webglcontextlost handler swaps to an SVG cone, and reduced-motion renders one settled static frame instead of the loop.

05 Iteration log

Pass 1 · Craft

Rebalancing the render

  • First build swept a single synchronised reveal across all paths — the shared growing front bloomed into a bright vertical curtain sliding right, reading as a light-bar, not a fan. Replaced it with a per-path phase so highlights desync and the cone stays whole.
  • Bloom was blowing the median into a laser. Dropped strength 0.86 → 0.52, raised threshold, and cut the median tube's HDR boost so it reads as a lit thread, not a beam.
  • Threads were too faint to show density; lifted base alpha and count to 1,100 so the additive core carries the probability shape.
  • The ledger's SDE was being uppercased, mangling σS dW into ΣS DW. Split it onto its own line in true case with proper subscripts.
Pass 2 · Depth

Second-read detail & motion

  • Added the scanning caliper and wired the ledger's band at t value to its position — the widening you watch and the number you read are now the same measurement.
  • Introduced a nine-second resample behind a crossfade so the exhibit shows genuinely new futures, not a frozen sample — real liveness for the passive two-frame check.
  • Tuned the travelling highlight to sparkle along threads like futures propagating outward, and added the glowing origin node so every path visibly leaves one point.
Pass 3 · Hardening

375px, motion, focus, performance

  • At 375px the lead sat over the bright core; strengthened the mobile scrim and floated the ledger into flow below the hero. Verified scrollWidth == innerWidth, no offenders.
  • Confirmed the reduced-motion path renders a full settled cone with the caliper parked at t = 0.62 — never a blank frame — and that no rAF loop starts.
  • Checked :focus-visible rings on every link, DPR-cap and resize re-fit, and the document.hidden pause with clamped delta.
  • Chanel rule: removed the orphaned period floating after the headline's √t superscript — a small ornament that read as a stray dot.