Kaizen Media · R&D floor
The Lab.
Client sites get the professional restraint. This page doesn't. One hundred hand-written experiments — no libraries, no plugins, no templates. Every exhibit opens up: read the maths, watch the live numbers, turn the dials. If you're wondering whether your project could do something like this: yes. That's the point.
View source if you think I'm bluffing
This background: a raw WebGL fragment shader · 0 libraries · 60fps
The catalogue
One hundred experiments.
All hand-written, all running in your browser, all explained. Pick a door.
Wing I
The first thirty
Where it started. Particles, physics, algorithms and one very confident terminal.
Singularity
Your cursor now has gravity. Physics wasn't in the brief. Click to collapse the field — it springs back, because everything I build recovers.
How: canvas 2D · sprung particles · inverse-square field
Under the hood
Every dot remembers its home. Each frame its target becomes home plus a pull toward the cursor, capped at min(42, 3000/d) — an inverse-distance field. A damped spring integrator chases that target, which is why the field overshoots and wobbles back instead of snapping.
const pull = Math.min(42, 3000 / dist);
const tx = d.bx + (dx / dist) * pull;
d.vx = (d.vx + (tx - d.x) * 0.14) * 0.82; // spring + damping
d.x += d.vx;
Shapeshifter
Two thousand particles with career ambitions. They re-organise every few seconds — click to force a reshuffle.
How: text rasterised offscreen → sampled targets → eased swarm
Under the hood
Each word is rasterised to an offscreen canvas, then alpha-sampled every 3px into a target list. Every particle is assigned a target with a prime-number stride (i × 7919 mod n) so neighbours scatter instead of clustering, then eases 7.5% of the remaining distance per frame.
if (img[(y * W + x) * 4 + 3] > 128) pts.push([x, y]);
// prime stride avoids clumping:
const t = pts[(i * 7919) % pts.length];
p.x += (p.tx - p.x) * 0.075;
The Playroom
Fully simulated physics. Grab a seal and throw it. It's fine. Probably.
How: hand-rolled physics — gravity, restitution, collisions, spin
Under the hood
Semi-implicit Euler with impulse collision response: overlapping seals are separated along the contact normal, then the relative velocity along that normal is reflected with restitution 0.82. Your throw velocity comes from a five-point pointer history sampled at release.
const rel = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny;
if (rel < 0) {
const imp = -rel * 0.82; // restitution
a.vx -= nx * imp; b.vx += nx * imp;
}
Live DNA
The CSS on the left styles the card on the right — live, as you type. Break it with confidence. Refresh forgives everything.
How: your keystrokes → a <style> tag → the browser does the rest
Your Website
Looks innocent. Fully editable. This is what "custom-built" actually means.
Powered by CSSUnder the hood
There is no trick. Your keystrokes land in a style tag and the browser's CSSOM re-cascades in real time. The demo card is just markup — which is the point: this is how every custom build works underneath.
input.addEventListener("input", () => {
styleEl.textContent = input.value;
}); // the browser does the rest
The Donut
A 3D torus rendered in text, spinning at 60fps. The oldest flex in graphics programming — done from scratch.
How: the classic donut maths · projected to characters · one <pre> tag
Under the hood
The legendary donut.c, reimplemented. Points on a torus (two angles), rotated by two rotation matrices, perspective-projected with 1/z, z-buffered per character cell, and lit by the surface normal — luminance indexes into the ramp .,-~:;=!*#$@.
const ooz = 1 / z; // perspective
const px = (COLS/2 + COLS*0.42*ooz*x) | 0;
if (ooz > zbuffer[idx]) { // nearest wins
out[idx] = CH[(L + 0.8) * 7 | 0]; // light ramp
}
The Flock
Ninety boids with three rules and no leader. Your cursor is the hawk.
How: separation, alignment, cohesion · O(n²) and proud
Under the hood
Three accumulators per boid, computed over every neighbour within 70px: separation (inverse-square push), alignment (velocity matching) and cohesion (pull to the local centroid). Your cursor injects a fourth force — flee — and speed is clamped to 60–170 px/s so panic still looks graceful.
if (d2 < 4900) { // neighbourhood
cx += o.x; avx += o.vx; n++; // cohesion + alignment
if (d2 < 900) sx -= dx / d2 * 24; // separation
}
if (pd < 130) flee(900 * (1 - pd / 130));
Storm
Procedural lightning. Click to call a strike down on the exact spot you're pointing at.
How: recursive midpoint displacement · two-pass glow
Under the hood
Recursive midpoint displacement: split the segment, shove the midpoint sideways by ±disp, halve disp, recurse. A 16% branch chance spawns forks with doubled offset. Each bolt is stroked twice — a fat red glow underneath and a thin hot core on top — then fades out in under half a second.
const mx = (ax + bx) / 2 + rand(-disp, disp);
sub(ax, ay, mx, my, disp / 2); // recurse left
sub(mx, my, bx, by, disp / 2); // recurse right
if (Math.random() < 0.16) branch(mx, my);
Fireworks
Click. Celebrate. Repeat. You've earned it — you scrolled this far.
How: launch physics · spark showers · additive blending
Under the hood
Rockets decelerate under gravity until apex, then burst into 70–130 sparks with random heading and speed. Sparks feel real because of three cheats: per-spark drag (0.985), gravity, and drawing onto a canvas that is faded instead of cleared — which is where the trails come from.
for (let k = 0; k < n; k++) {
const a = Math.random() * TAU;
sparks.push({ vx: Math.cos(a) * sp, vy: Math.sin(a) * sp });
}
fade(0.2); // translucent fill = free motion trails
Currents
Two thousand particles riding an invisible wind. Click to change the weather.
How: value-noise flow field · advected particles
Under the hood
A value-noise field turned into wind: the angle at any point is noise(x, y, t) × 4π. Sixteen hundred particles are advected through it at 70px/s, each drawing a 1px segment from where it was to where it is. The canvas fades 5.5% per frame, so the rivers linger.
const a = vnoise(p.x * .004 + seed, p.y * .004 - t * .07) * TAU * 2;
p.x += Math.cos(a) * 70 * dt;
p.y += Math.sin(a) * 70 * dt;
// click: seed += 37.7 → new weather
Network
Everything connects when it gets close enough. Including you — move in.
How: distance-based edges · drifting nodes
Under the hood
The classic constellation, hand-rolled: every pair of nodes is tested each frame (O(n²), 3 570 pairs, still trivial for a modern CPU) and edges fade in with proximity. Edges to your cursor get the brand treatment.
if (d2 < 110 * 110) {
const a = 1 - Math.sqrt(d2) / 110;
stroke(`rgba(237,235,228,${a * 0.28})`);
}
Downpour
It's raining the brand. Someone had to do it.
How: column glyph rain · translucent trails
Under the hood
One object per column: a head position and a fall speed. The head glyph is drawn bright, one behind it in red — and that's it. The iconic trails aren't stored anywhere; they're the ghost of previous frames left behind by a 13% translucent fade.
fade(0.13); // the trail IS the fade
ctx.fillStyle = "rgba(255,240,228,0.9)";
ctx.fillText(randomGlyph(), c.x, c.y);
ctx.fillStyle = "rgba(217,58,43,0.6)";
ctx.fillText(randomGlyph(), c.x, c.y - 15);
Ripples
Click to drop a stone. The waves interfere like they paid attention in physics class.
How: summed sine waves · exponential decay
Under the hood
Textbook wave interference: the height at any point is the sum over every stone of sin(0.11d − 7·age) attenuated by e^(−0.006d − 0.9·age). Sampled on a 15px grid; |height| maps to dot size and brightness, and crests above 0.55 turn red.
let h = 0;
for (const w of waves) {
const d = Math.hypot(x - w.x, y - w.y);
h += Math.sin(d * .11 - age * 7)
* Math.exp(-d * .006 - age * .9);
}
Lava
A lava lamp with no lava, no lamp and no shame.
How: metaballs via blur + contrast thresholding
Under the hood
Metaballs without marching squares: blobs are drawn as soft radial gradients at quarter resolution, then composited through blur(14px) contrast(24). The blur merges their fields; the contrast thresholds the result into clean liquid edges. Two CSS filters doing the work of an isosurface algorithm.
o.fillStyle = radialGradient(x, y, r); // soft blob
o.arc(x, y, r, 0, TAU); o.fill();
ctx.filter = "blur(14px) contrast(24)"; // the magic
ctx.drawImage(off, 0, 0, W, H);
The Flag
Cloth simulation, waving at you. Sweep your cursor through it.
How: verlet grid · pinned edge · procedural wind
Under the hood
Verlet integration: each point stores only position and previous position — velocity is implied by their difference, which makes cloth stable almost for free. Three constraint iterations per frame pull neighbours back to rest spacing. The wind is noise-scaled force, and your cursor transfers its velocity on contact.
const vx = (p.x - p.px) * 0.985; // implied velocity
p.px = p.x; p.x += vx + wind * dt * dt;
// constraint: pull neighbours to rest distance
const diff = (d - spacing) / d / 2;
p.x += dx * diff; o.x -= dx * diff;
Slack
A rope with real slack. Grab the middle and misbehave.
How: verlet chain · distance constraints
Under the hood
The same verlet trick as the flag, but run through fourteen constraint iterations — more iterations equals stiffer rope. The grabbed node is pinned to your pointer during the solve, and both anchors stay locked, so tension propagates realistically along the chain.
for (let iter = 0; iter < 14; iter++) // stiffness
for (let i = 0; i < n - 1; i++)
satisfy(pts[i], pts[i + 1], restLen);
if (grabbed) pts[g].set(pointer); // hard pin
Chaos
Five double pendulums, born identical. Watch them disagree — that's chaos theory. Click to rewind time.
How: coupled pendulum equations · trail persistence
Under the hood
The full double-pendulum equations of motion — the 2 − cos(2Δ) denominator form — integrated with three substeps per frame for stability. The five pendulums start 0.002 radians apart. Watching their trails split is watching sensitive dependence on initial conditions, live.
const den = 2 - Math.cos(2 * (a1 - a2));
const acc1 = (-g * (2*sin(a1) + sin(a1 - 2*a2)) / L
- 2*sin(Δ) * (w2² + w1² * cos(Δ))) / den;
// five copies, 0.002 rad apart → divergence
Orrery
Click to throw planets at a sun. Some orbit. Some don't. That's gravity's problem, not mine.
How: Newtonian attraction · velocity from your throw point
Under the hood
Newtonian gravity toward the sun: a = GM/d², semi-implicit Euler. Thrown planets get a tangential velocity of √(GM/d) times a random 0.75–1.1 — exactly circular-orbit speed, fuzzed. Under it: stable orbits, ellipses and the occasional dramatic escape.
const a = GM / (d * d); // inverse square
p.vx += (dx / d) * a * dt;
// spawn: tangent × √(GM/d) ≈ orbital velocity
const v = Math.sqrt(GM / d) * rand(0.75, 1.1);
Life
Conway's Game of Life. Drag across it to paint new cells and play god a little.
How: cellular automaton · toroidal grid
Under the hood
Conway's B3/S23 on a toroidal Uint8Array, double-buffered, stepped every fourth frame. An age array colours newborn cells red, fading toward paper as they survive. If population drops below 1.5% the grid quietly reseeds — extinction is off-brand.
buf[i] = alive
? (n === 2 || n === 3 ? 1 : 0) // survival
: (n === 3 ? 1 : 0); // birth
grid.set(buf); // double buffer
The Wayfinder
It builds a maze, then solves it, then does it all again. Forever. Some of us just enjoy work.
How: recursive backtracker · breadth-first flood
Under the hood
Two classic algorithms chained: a stack-based recursive backtracker knocks down walls to carve a perfect maze, then breadth-first search records every cell's distance from the entrance. The flood you watch is the BFS visit order replayed six cells per frame; the path walks backwards down the distance gradient.
// carve: random unvisited neighbour, knock wall
walls[cur][w1] = 0; walls[next][w2] = 0;
// solve: BFS, then descend the gradient
if (dist[n] === dist[cur] - 1) cur = n;
Order
Sixty bars sorted before your eyes, endlessly. Deeply satisfying. Mildly hypnotic.
How: quicksort, replayed swap by swap
Under the hood
The quicksort runs once, instantly, on a hidden copy — recording every swap into a log. What you watch is the log replayed four swaps per frame on the visible array. Visualising algorithms is mostly time travel.
qs(0, n - 1); // real sort, recorded
swaps.push([i, j]); // the log
// replay: 4 swaps/frame on the display array
[v[a], v[b]] = [v[b], v[a]];
Warp
Punch it. Steer with your cursor.
How: z-projected starfield · speed by steering
Under the hood
Stars live in a unit cube with a shrinking z. Screen position is (star − steering) / z × focal — as z approaches zero the division flings positions outward, which is the entire hyperspace effect. Each streak is a line from the previous frame's projection to this one's.
st.z -= speed * dt * 0.45;
const sx = W/2 + (st.x - steer) / st.z * f;
// line from last frame's projection = streak
ctx.moveTo(prevX, prevY); ctx.lineTo(sx, sy);
The Tail
A creature made of springs that thinks your cursor is food.
How: spring chain · follow constraints
Under the hood
The head is a damped spring chasing your cursor. Every segment behind it is solved with one-pass inverse kinematics: place yourself at a fixed distance along the direction to your predecessor. Tapered radii and two eyes on the velocity vector do the character design.
head.v = (head.v + (target - head) * .06) * .86;
for (const seg of segs) {
const d = dist(seg, prev);
seg.pos = prev + (seg - prev) / d * LINK; // IK
}
Filings
Iron filings, minus the iron and the mess. You're the magnet.
How: vector field · eased rotation
Under the hood
Each filing eases its angle toward atan2(dy, dx) + 90° — the tangent of a circle around your cursor, which is roughly what iron does around a pole. The easing uses shortest-path angle wrapping so filings never spin the long way round.
let target = Math.atan2(dy, dx) + Math.PI / 2;
let diff = target - f.a;
while (diff > Math.PI) diff -= TAU; // shortest arc
f.a += diff * 0.1;
Ridgelines
A landscape of pure noise, drifting forever. Album cover sold separately.
How: layered value noise · painted occlusion
Under the hood
Seventeen rows, drawn top to bottom. Each is a value-noise line whose amplitude is shaped by a gaussian envelope (big in the middle, flat at the edges). Filling below every line with the background colour before stroking it paints the occlusion — fake 3D for the price of a fill call.
const amp = Math.exp(-cx * cx) * 90; // gaussian
const y = baseY - vnoise(x * .008 + row * 3.7, t) * amp;
ctx.fill(); // background fill = occlusion
ctx.stroke(); // then the ridge line
The Plotter
A harmonograph drawing itself into exhaustion, then starting over. Relatable.
How: damped sine pairs · incremental path
Under the hood
A harmonograph: x(t) and y(t) are each sums of two damped sine waves with random frequencies and phases. Seventy points are appended per frame with no clearing, so the pen literally draws. When the decay envelope dies below 4%, it picks new parameters and starts again.
x = A1 * sin(f1*t + p1) * e^(-d1*t)
+ A2 * sin(f2*t) * e^(-d2*t);
// 70 points/frame, never cleared — a real pen
if (Math.exp(-d1 * t) < 0.04) restart();
Corner Watch
The bouncing logo. You know exactly why you're still watching. The counter shares your hope.
How: reflection physics · corner detection · communal disappointment
Under the hood
Pure reflection physics — velocity components flip independently at the walls. A corner hit is both axes clamping in the same frame, and the maths makes that genuinely rare, which is why the counter matters. The tint cycles on every wall hit, as tradition demands.
if (x <= 0 || x + s >= W) { vx *= -1; hitX = true; }
if (y <= 0 || y + s >= H) { vy *= -1; hitY = true; }
if (hitX && hitY) corners++; // the prophecy
Pong, obviously
You versus a very smug AI. The left paddle is yours — move your cursor. Try not to lose to maths.
How: playable canvas game · the AI is imperfect on purpose
Under the hood
The ball gains 6% speed per return and deflects by where it strikes the paddle (vy += offset × 6), so placement is strategy. The AI chases the ball with a capped speed plus a sine wobble — deliberately imperfect. If it never lost, you'd stop playing.
d.bvx = Math.abs(d.bvx) * 1.06; // speed up
d.bvy += (ballY - paddleY) * 6; // spin
const target = ball.y + sin(t*3) * 30; // AI wobble
ai.y += clamp(target - ai.y, ±maxSpeed);
The Instrument
Twelve pads, pentatonic, impossible to play badly. Compose your masterpiece.
How: Web Audio oscillators · envelope shaping · no samples
Under the hood
One triangle oscillator per press, shaped by a gain envelope — an exponential ramp up in 12 milliseconds and out over 650. No samples, no files: the browser synthesises every note. The scale is pentatonic, so wrong notes are mathematically impossible.
const osc = actx.createOscillator();
osc.type = "triangle"; osc.frequency.value = f;
gain.exponentialRampToValueAtTime(0.22, t + 0.012);
gain.exponentialRampToValueAtTime(0.0001, t + 0.65);
Scratch
Scratch the panel. There's something underneath. I'm not telling you what.
How: destination-out erasing · an actual secret
Mention the word SCRATCH when you enquire and your first month of Kaizen Care is 10% off. Our secret.
Under the hood
Your pointer draws circles with globalCompositeOperation destination-out, which erases alpha instead of painting. Every 0.8 seconds a strided getImageData pass counts how much is gone; past 45% the panel concedes and fades itself out.
ctx.globalCompositeOperation = "destination-out";
ctx.arc(P.x, P.y, 26, 0, TAU); ctx.fill(); // erase
// strided sample: cleared / total > 0.45 → reveal
The Terminal
A terminal on a marketing site. Type help. Yes, it answers. Yes, you can try sudo.
How: hand-rolled shell · zero regrets
Under the hood
A dictionary of commands, one input and an HTML printer. That's the whole shell. The sudo response is hand-written because the alternative was you not trying it, and we both know that was never going to happen.
const CMDS = { help, whoami, hire, sudo, coffee };
const fn = CMDS[cmd];
fn ? fn() : print(`command not found: ${cmd}`);
Wing II
Nature & particles
Weather, wildlife and other things that were not in the hosting package.
Spiral Galaxy
Forty thousand years of rotation, compressed. The arms are a lie your eyes tell you — every star just orbits at its own speed.
How: canvas 2D · differential rotation
Under the hood
Stars sit at random radii with angular velocity proportional to 1/√r — inner stars lap outer ones and the density waves your brain reads as arms emerge on their own.
w = 0.9 / Math.sqrt(r);
a += w * dt; // inner stars lap outer ones
x = cx + Math.cos(a + armOffset) * r;
Snowfall
It settles. Give it a minute and watch the drifts build. South Africans, this is what it looks like.
How: particle fall · height-map accumulation
Under the hood
Each flake falls with noise-driven sway. The floor is a height-map array: when a flake lands, its column grows — which is why drifts form under where the wind funnels them.
if (f.y >= H - ground[ix]) {
ground[ix] += 2.4; // the drift grows
respawn(f);
}
Rain, Properly
Streaks fall, hit the ground and splash. The most South African weather on the page.
How: velocity streaks · splash particles
Under the hood
Drops are drawn as motion streaks (a line along their velocity). On impact each spawns three or four short-lived splash particles with upward velocity — that detail is 90% of the realism.
ctx.moveTo(d.x, d.y);
ctx.lineTo(d.x - d.vx * .02, d.y - d.vy * .02);
if (d.y > ground) splash(d.x, 3 + rand(2));
Bubbles
They rise, they wobble, they pop when you touch them. Deeply unnecessary. Completely required.
How: sine wobble · burst rings
Under the hood
Bubbles rise with a phase-offset sine sway. Touching one replaces it with an expanding, fading ring — the pop is one arc with a growing radius and a dying alpha.
b.x += Math.sin(t * b.wf + b.ph) * 0.6;
if (dist(P, b) < b.r) pop(b); // ring: r += 140*dt, a -= 3*dt
The Swarm
A hundred workers who want to be near your cursor but keep overshooting. Relatable energy.
How: steering acceleration · momentum
Under the hood
Each bee accelerates toward the cursor but keeps its momentum, so it orbits and overshoots instead of arriving — steering behaviour with deliberately bad brakes.
const a = 900 / Math.max(40, d);
b.vx += (dx / d) * a * dt;
b.vx *= 0.985; // barely any brakes
Slipstream
A river of particles that refuses to touch you. Move through it and watch it part around your cursor like you're famous.
How: flow + radial deflection
Under the hood
Particles stream left to right; near the cursor their velocity gains a perpendicular component scaled by 1/d — the same maths as air around a wing, give or take a physics degree.
const push = 2600 / (d * d);
p.vx += (dx / d) * push; // radial deflection
p.vy += (dy / d) * push;
Sparkler
Hold and drag. You're writing with fire now. It fades because all the best things do.
How: emission from pointer · gravity + fade
Under the hood
While the pointer is down it emits 12 sparks per frame with random velocity, gravity and a lifetime. The trail is the usual translucent-fade trick — nothing is stored, everything is memory.
if (P.down) for (let i = 0; i < 12; i++)
sparks.push({ x: P.x, y: P.y,
vx: rand(-140, 140), vy: rand(-180, 60) });
Nebula Brush
Paint with clouds of ember light. This one keeps your work — it's the only canvas here that respects your art.
How: additive radial brushes · persistent canvas
Under the hood
Each drag stamp is a soft radial gradient composited with 'lighter', so overlapping strokes sum into brightness. The canvas is never cleared and is exempt from the memory reclaimer.
ctx.globalCompositeOperation = 'lighter';
g = radialGradient(x, y, 40); // soft ember
// never cleared: cfg.keep = true
Sandfall
A falling-sand automaton. Click and hold to pour. The pile knows how to be a pile — nobody taught it.
How: cellular automaton · granular rules
Under the hood
Every grain checks down, then down-left or down-right, one cell per tick. That three-line rule is the entire physics of sand — slope angles and avalanches are emergent.
if (!g[below]) move(below);
else if (!g[belowL]) move(belowL);
else if (!g[belowR]) move(belowR); // that's sand
Langton's Ants
Four ants, two rules each: turn by the colour, flip the colour, step. Chaos for ten thousand steps — then highways. Nobody knows why. Genuinely: nobody knows.
How: turmite automaton · emergent highways
Under the hood
On white turn right, on black turn left, flip the cell, move forward. After ~10 000 steps of apparent chaos every Langton's ant builds a diagonal highway — an unproven-in-general, universally observed mystery.
const c = grid[i];
dir = (dir + (c ? 3 : 1)) % 4; // L or R
grid[i] = 1 - c; step();
Wing III
The physics department
Everything falls, swings, bounces or explodes. As nature intended.
Newton's Cradle
Drag the end ball, let go and watch momentum do its office-desk thing. Yes, pulling two works.
How: pendulum physics · momentum transfer
Under the hood
Each ball is a pendulum solved with angular acceleration −g/L·sin θ. Collisions between neighbours swap angular velocities — which for equal masses is exactly what elastic collision maths collapses to.
a.acc = -g / L * Math.sin(a.th);
if (touching(a, b) && a.w > b.w)
[a.w, b.w] = [b.w, a.w]; // swap momenta
The Trampoline
Balls raining on a stretchy line. The line disagrees with gravity on behalf of the balls.
How: verlet string · penetration response
Under the hood
The trampoline is a pinned verlet string. When a ball penetrates it, the nearest points get pushed down (stretching the string) and the ball gets the string's displacement back as upward velocity next frame.
if (ball.y + r > lineY(ball.x)) {
pt.y += push * 0.5; // string absorbs
ball.vy -= push * 26; // and returns it
}
Wrecking Ball
A pendulum with a job. The crates rebuild because destruction should be renewable.
How: pendulum + impulse knockdown
Under the hood
The ball is a driven pendulum; crates are boxes that take an impulse when the ball's swept circle crosses them, with torque from the hit offset. They respawn after a decent interval of rubble.
if (hit(ball, crate)) {
crate.vx += ball.vx * 0.8;
crate.vr += (crate.y - ball.y) * 0.02;
}
Dominoes
Click the first one. You know you want to. The chain reaction is 14 rectangles and one rule.
How: rotation cascade · contact tipping
Under the hood
Each domino rotates around its base corner once triggered. When its top edge sweeps into the next one's space, that one starts tipping too — the delay between falls is just geometry.
if (d.a > TRIGGER_ANGLE)
next.falling = true; // contact by sweep
d.a += d.va * dt; d.va += 4.2 * dt;
Buoyancy
Balls in water. They bob because physics says so: displaced water pushes back exactly as hard as it was displaced.
How: sine surface · Archimedes force
Under the hood
The water surface is summed sines. Submerged depth drives an upward force proportional to displacement (Archimedes), drag slows everything, and the bobbing frequency falls out for free.
const depth = ball.y - surfaceAt(ball.x);
if (depth > 0) ball.vy -= depth * 18 * dt; // buoyancy
ball.vy *= 0.985; // drag
Charged
Red repels red, attracts pale — Coulomb's law with the numbers filed off. Click to flip every polarity mid-flight.
How: inverse-square attraction/repulsion
Under the hood
Every pair feels a force ±k/d² along their separation vector, sign by charge product. Clicking negates all charges, which instantly turns clusters into explosions — physics' best party trick.
const f = 900 * qa * qb / (d * d);
a.vx -= (dx / d) * f * dt;
// click: charges.forEach(c => c.q *= -1)
The Gearbox
Five meshing gears. Tooth counts set the speeds — small ones spin like interns, the big one like management.
How: parametric gear drawing · ratio chain
Under the hood
Each gear is drawn parametrically (teeth as radial bumps). Angular velocity chains through the mesh: ω₂ = −ω₁ × T₁/T₂ — the minus is why adjacent gears counter-rotate.
g2.w = -g1.w * g1.teeth / g2.teeth;
// teeth: r + bump * sin(teeth * angle)
The Chain
A hanging chain of rigid links. Grab anywhere and swing it. Satisfyingly heavy for something made of maths.
How: verlet + distance joints
Under the hood
Fifteen verlet points with hard distance constraints (eight solver iterations) make the links rigid. Mass is fake — heaviness comes entirely from damping and gravity tuning.
for (let k = 0; k < 8; k++) // rigidity
links.forEach(satisfyDistance);
p.y += (vy + G * dt) * dt; // weight
Soft Body
A pressurised blob. Poke it, drag it, watch it wobble back into shape like nothing happened. Emotional resilience, rendered.
How: spring skin + gas pressure
Under the hood
Skin nodes connect to neighbours with springs; an internal pressure force pushes outward along each node's normal, scaled by how compressed the area is versus rest. Springs pull in, pressure pushes out — the argument is the wobble.
const pressure = REST_AREA / area();
node.f += normal * pressure * 320;
spring(node, next); spring(node, prev);
The Engine
Crankshaft, rod, piston — the linkage that moved the whole twentieth century, idling at 90 rpm on a marketing site.
How: crank-slider kinematics
Under the hood
Pure kinematics: the crank pin moves on a circle and the piston position solves the rod-length triangle. One line of trig every engine textbook opens with.
px = crank * cos(a) +
Math.sqrt(rod² - (crank * sin(a))²);
// piston slides, rod connects, done
Wing IV
The mathematics wing
Two thousand years of theorems, animated without permission.
Pathfinder Race
Two solvers, same maze, no mercy: breadth-first (thorough, slow) versus greedy best-first (fast, occasionally embarrassing). New maze every round.
How: BFS vs greedy · same maze
Under the hood
Both search the same maze in parallel strides. BFS explores in distance order and guarantees the shortest path; greedy chases the goal by straight-line distance and sometimes pays for its optimism. The scoreboard keeps receipts.
bfs: expand FIFO — optimal, patient
greedy: expand min h(n) — fast, cocky
wins.bfs++ or wins.greedy++
Monte Carlo π
Throwing darts at a square to measure a circle. The estimate at the top gets better forever and is never quite right — π respects the grind.
How: random sampling · ratio estimate
Under the hood
Random points land in a square; the fraction inside the inscribed circle approaches π/4. Accuracy grows with √n, which is why the third decimal takes forever — a very honest algorithm.
if (x*x + y*y <= 1) inside++;
pi = 4 * inside / total; // converges as 1/√n
The Sieve
Eratosthenes, live: each prime sweeps through and knocks out its multiples. What survives, glows. 2 200 years old and still undefeated.
How: Eratosthenes · animated strike-out
Under the hood
The grid strikes multiples of each prime in turn — the animated version of the fastest simple way humanity has to find primes. Red sweeps are composites dying; the pale survivors are the primes.
for (m = p * p; m <= N; m += p)
composite[m] = true; // struck
// what's never struck is prime
Golden Spiral
Fibonacci squares snapping into place, a quarter-circle each. The spiral your sunflower already knew.
How: square tiling · quarter arcs
Under the hood
Each square's side is the sum of the previous two; each contributes a quarter arc. The arcs approximate a golden spiral because consecutive Fibonacci ratios converge on φ.
side = fib[i]; rotate placement 90°;
arc(corner, side, quarter);
// fib[i+1]/fib[i] → 1.618…
Mandelbrot
The most famous fractal, rendered live. Click anywhere to dive — every zoom recomputes from raw iteration, no images anywhere.
How: escape-time iteration · click zoom
Under the hood
For every pixel, iterate z = z² + c until it escapes or the budget runs out; the escape speed picks the colour. Clicking re-centres and shrinks the window by 3× — computed fresh, because storing images would be cheating.
while (x*x + y*y < 4 && i < MAX) {
[x, y] = [x*x - y*y + cx, 2*x*y + cy];
i++;
} // i chooses the colour
Julia Drift
The Mandelbrot's moodier sibling, morphing forever as its seed walks a circle. No two frames identical, ever.
How: escape-time · orbiting seed
Under the hood
Same iteration as the Mandelbrot but c is a constant shared by all pixels — and here c orbits slowly, dragging the whole set through its family of shapes. Rendered at low resolution and upscaled, because art forgives pixels.
c = 0.7885 * exp(i * t * 0.1); // the seed orbits
z = z*z + c; // per pixel
Epicycles
Draw any shape. Watch a chain of spinning circles redraw it perfectly. This is the Fourier transform — the maths inside every song you've streamed — drawing with compasses.
How: hand-rolled DFT · rotating vector chain
Under the hood
Your stroke is resampled and pushed through a discrete Fourier transform, decomposing it into rotating vectors sorted by amplitude. Summed tip-to-tail they retrace your drawing — proof that any squiggle is secretly a sum of circles.
X[k] = Σ p[n] · e^(−i2πkn/N) // your drawing
// replay: chain circles, each spinning at freq k
tip = Σ X[k] · e^(i2πkt)
Prime Spiral
Count in a spiral, light the primes. The diagonals appear immediately and mathematics still can't fully explain them. Sleep well.
How: Ulam spiral · trial division
Under the hood
Numbers spiral outward from centre; primes get a dot. The diagonal streaks correspond to prime-rich quadratics like n²+n+41 — observed since 1963, still without a complete explanation.
step the spiral: R, U, L, L, D, D, R, R, R…
if (isPrime(n)) glow(x, y);
// the diagonals are the mystery
Bézier Anatomy
Drag the four handles. The moving construction lines are de Casteljau's algorithm — the reason every font, logo and car body looks smooth.
How: de Casteljau construction · draggable
Under the hood
A cubic Bézier point is nested linear interpolation: lerp the four controls pairwise, then those results, then once more. The animated scaffolding shows all three layers meeting at the curve point.
a = lerp(p0, p1, t); b = lerp(p1, p2, t);
d = lerp(a, b2, t); // …and once more
curve(t) = the last lonely point
Voronoi
Every pixel belongs to its nearest seed. Click to add seeds and redraw the borders. This is how leopard spots, ecology and mobile-tower coverage all think.
How: nearest-seed regions · drifting sites
Under the hood
Each coarse cell asks which drifting seed is closest and takes its colour; boundaries are where two seeds tie. Computed brute-force per frame because n is small and honesty is cheap.
for (each cell) best = argmin dist(cell, seed);
// borders = ties; click pushes a new seed
Noise Worms
Blind worms steering by an invisible field, leaving ink where they've been. They never agree on a direction and it's better that way.
How: noise-steered agents · persistent trails
Under the hood
Each worm's heading eases toward the value-noise angle at its position — same field as the Currents exhibit, but the trail persists, so you watch the field's topology get slowly discovered.
h += (noiseAngle(x, y, t) - h) * 0.08;
x += cos(h) * v; // ink stays: no clear
Reaction–Diffusion
Two chemicals, four rules, and suddenly: coral, fingerprints, leopard print. Gray–Scott, computed live. Turing predicted this in 1952 and nobody believed him.
How: Gray–Scott model · live grid
Under the hood
Chemical A feeds in, B eats A and B decays, both diffuse at different rates. Iterated on a coarse grid several times per frame, those rules alone paint every animal you've ever seen — Turing patterns, live.
A' = A + (dA·∇²A − AB² + f(1−A))
B' = B + (dB·∇²B + AB² − (k+f)B)
// pattern = f and k, nothing else
Hailstone Numbers
Pick a number. Halve it if even, triple-and-add-one if odd. Every path ever tried crashes into 1 — and proving it always does is still an open problem worth its own Fields Medal.
How: Collatz paths · overlaid orbits
Under the hood
Random starters have their full hailstone path traced as a polyline (height = value, log scale). The Collatz conjecture — that every path reaches 1 — has been checked past 2⁶⁸ and proven never.
n = n % 2 ? 3 * n + 1 : n / 2;
path.push(n); // always ends at 1. probably.
Binary Clock
The actual time, right now, in base 2. Learn to read it and you'll never be invited to parties again.
How: real time · bit dots
Under the hood
Hours, minutes and seconds are drawn as bit columns — each dot is one bit of the current time. The blink you see every second is the least significant bit doing its job.
const bits = value.toString(2);
dot.lit = bits[i] === '1';
// LSB flips every second, as it must
The Cipher Wheel
Caesar's cipher, twenty-two centuries later: type a message, drag the shift, watch it scramble. Rome fell; the algorithm didn't.
How: Caesar shift · live rot-N
Under the hood
Each letter shifts N places around the alphabet, wrapping at Z — encryption a schoolchild can break and an emperor trusted. The slider is the entire key space, which is precisely the problem.
out = ((code - 65 + shift) % 26) + 65;
// key space: 25. brute force: instant.
Wing V
Type & text
Letters that refuse to sit still.
The Wave
A sentence doing stadium duty. Every letter surfs its own offset of the same sine — which is all a wave has ever been.
How: per-letter sine offsets
Under the hood
The phrase is drawn letter by letter, each with y = sin(t·speed + index·gap)·amp. Coherent motion from independent parts — the entire concept of a wave in one line.
y = Math.sin(t * 3 + i * 0.4) * 18;
ctx.fillText(ch, x, baseY + y);
Gravity Type
A perfectly good headline until you click it. Letters become bodies; bodies obey gravity; gravity has no respect for typography. Click again to rebuild.
How: letters as rigid bodies
Under the hood
Each glyph is measured, positioned, then — on click — handed velocity, spin and gravity, bouncing on the floor until it settles into rubble. A second click restores the sentence, because kaizen.
letters.forEach(L => {
L.vy += G * dt; L.rot += L.vr * dt;
if (L.y > floor) bounce(L);
});
Orbit Type
Three rings of text rotating at different speeds. Words in orbit read differently — that's not deep, it just looks great.
How: text on circles · counter-rotation
Under the hood
Each ring places its characters around a circle with per-character rotation, then the whole ring turns — outer clockwise, middle counter, inner slow. The renderer is save/rotate/translate/fillText, forty times.
ctx.rotate(ringAngle + i * step);
ctx.fillText(ch, 0, -radius);
// three rings, alternating direction
The Haiku Machine
Industrial poetry: five-seven-five about code, clients and continuous improvement. Press for a fresh one. Quality not guaranteed; syllables are.
How: word banks · 5-7-5 assembly
Under the hood
Three line templates draw from word banks whose entries carry syllable counts, so every poem scans 5-7-5. Meaning is the reader's job — same as all poetry.
line = pick(fives) + pick(sevens) + pick(fives);
// syllables counted; profundity emergent
ASCII Storm
The nebula shader's poor cousin: live value noise rendered as characters, because pixels are just letters that gave up.
How: noise field → character ramp
Under the hood
Value noise sampled on a character grid indexes into a density ramp from space to @. It's the Donut's lighting trick applied to weather — proof that any scalar field can be typeset.
const v = vnoise(x * .08, y * .1 + t * .3);
row += RAMP[(v * RAMP.length) | 0];
Wing VI
The arcade
Playable. Score-keeping. Mildly addictive. You were warned.
Breakout
1976 called; it's still fun. Cursor moves the paddle. The bricks are load-bearing — the wall rebuilds when you win.
How: AABB reflection · brick grid
Under the hood
The ball reflects off axis-aligned boxes; which axis flips depends on the shallowest overlap. Paddle hits add english proportional to strike offset — the entire skill ceiling of the genre in one line.
if (overlapX < overlapY) ball.vx *= -1;
else ball.vy *= -1;
ball.vx += (ball.x - paddle.x) * 3.2; // english
Snake, Self-Taught
The snake plays itself: breadth-first search to the food, every move, forever. It will still eventually trap itself, because self-confidence is not pathfinding.
How: BFS autopilot · grid snake
Under the hood
Before each move the snake BFS-searches from head to food, treating its own body as walls. Optimal until the board fills and its body becomes the maze — machine hubris, visualised.
const path = bfs(head, food, body);
move(path ? path[0] : straight());
// it dies like the rest of us: boxed in
Minesweeper Mini
Nine by nine, ten mines, no flags — commitment only. First click is always safe because this house is merciful.
How: flood reveal · deferred mine placement
Under the hood
Mines are placed only after your first click, excluding it — that's the classic mercy rule. Zero-cells flood-fill their neighbours recursively, which is why one click can crack half the board.
if (firstClick) placeMines(except = cell);
if (count(cell) === 0) neighbours.forEach(reveal);
Simon Says
Four pads, one growing sequence, your working memory versus a while-loop. The tones are synthesised live — even the beeps are hand-made.
How: sequence memory · WebAudio tones
press any pad to begin
Under the hood
Each round appends a random pad and replays the whole sequence with synthesised tones (one oscillator per note). Your job is echoing it back; the loop's job is outlasting you. It will.
seq.push(rand4());
playback(seq); // osc per pad: 330/392/494/587Hz
if (input[i] !== seq[i]) gameOver();
Whack-a-Seal
Seals pop out of holes; you bonk them with clicks. Thirty seconds on the clock. The high score is a personality test.
How: spawn windows · hit timing
Under the hood
Seals rise from random holes on a shrinking timer, hittable only while up. Score-chasing is pure human-benchmark: the spawn window tightens 4% per hit until your mouse hand files a complaint.
if (up && clickIn(hole)) { score++;
window *= 0.96; } // it gets worse for you
Flappy Seal
Click to flap. The pipes are procedurally heartless. Nobody has ever quit this genre feeling good and you won't either.
How: impulse flap · scrolling gates
Under the hood
Gravity pulls, clicks set a fixed upward velocity, pipe gaps scroll past at constant speed. The genre's cruelty is that both your inputs and the physics are perfectly deterministic — every death is yours.
if (click) seal.vy = -320; // the flap
seal.vy += 900 * dt; // the fall
if (hit(pipe)) restart(); // the truth
The Reflex Test
Wait for red to turn washi. Click. Get judged in milliseconds. Under 200 and you should be doing esports instead of procurement.
How: timestamp delta · anti-cheat
Under the hood
A random 1–3s wait arms the trigger; clicking early resets it with appropriate shame. The measurement is one subtraction — performance.now() at colour-flip versus at click.
armTime = now + rand(1000, 3000);
result = clickTime - goTime; // that's you, in ms
The Typing Test
Type the sentence. Live words-per-minute, live accuracy, no autocorrect to save you. The sentence is about kaizen because of course it is.
How: per-keystroke diff · live WPM
0 wpm · 100% accuracy
Under the hood
Every keystroke diffs your input against the target — greens, reds and a WPM computed as (correct chars / 5) per elapsed minute, the standard nobody agreed on but everyone uses.
wpm = (correct / 5) / (elapsedMs / 60000);
span.class = typed === target ? 'ok' : 'err';
Memory Pairs
Sixteen cards, eight glyph pairs, one working memory. The cards flip with a fake 3D scale because drama matters.
How: state machine · match logic
Under the hood
A tiny state machine: at most two cards face-up; a match locks them, a miss flips both back after 700ms of public embarrassment. The flip is scaleX through zero — cardboard 3D.
open.push(card);
if (open.length === 2)
match() ? lock() : setTimeout(flipBack, 700);
Tic-Tac-Toe
You're X. The AI runs full minimax, which means it has already seen every game that will ever be played, including this one. Best possible outcome: a draw. Good luck anyway.
How: full minimax · unbeatable
Under the hood
Minimax recursively scores every reachable position — win +10, loss −10, depth-adjusted so it prefers quick wins and slow losses. Noughts and crosses has 5 478 positions; the machine holds all of them at once.
best = max(moves.map(m =>
-minimax(apply(m), depth + 1)));
// it cannot lose. it knows it cannot lose.
Wing VII
Other dimensions
3D, illusions and assorted lies your eyes will enjoy.
The Cube
A wireframe cube you can grab and throw into a spin. Eight points, twelve lines and two rotation matrices — 3D from scratch.
How: rotation matrices · perspective divide
Under the hood
The vertices are rotated by X and Y matrices, then projected by dividing by depth. Drag velocity becomes angular velocity with momentum — the throw is the feature.
y1 = y * cosX - z * sinX; z1 = y * sinX + z * cosX;
sx = x1 / (z1 + dist) * scale; // perspective
Point Sphere
Four hundred points arranged by sunflower maths, spinning as a hollow globe. Drag it. The back is dimmer because depth cueing is cheap and effective.
How: fibonacci sphere · depth shading
Under the hood
Points are distributed by the golden-angle spiral (the only way to spread points evenly on a sphere without a committee), rotated in 3D and alpha-faded by depth.
const phi = Math.acos(1 - 2 * (i + .5) / N);
const theta = i * 2.399963; // golden angle
alpha = 0.2 + 0.8 * (z + 1) / 2;
The Tunnel
Concentric rings falling toward you forever. Your cursor bends the tunnel because straight tunnels are for trains.
How: ring recycling · offset lerp
Under the hood
Rings shrink toward a vanishing point that eases toward your cursor; when one collapses it respawns at the far end. Fifteen circles pretending to be infinity, successfully.
r *= 0.985; // approach
if (r < 2) r = MAX_R; // recycle
centre = lerp(centre, cursor, 0.03);
Moiré
Two identical line gratings, one slowly rotating. The giant sweeping bands don't exist in either layer — they're an interference pattern your eyes compute for free.
How: overlaid gratings · interference
Under the hood
Layer one is static lines; layer two is the same grating rotated by a slowly oscillating angle. The bands are spatial beat frequencies — the same maths as two guitar strings slightly out of tune.
drawGrating(0);
drawGrating(Math.sin(t * 0.1) * 0.12);
// the bands are in your head. really.
Depth Wobble
A 3D point cloud with no glasses required: the whole scene wiggles a few degrees and your brain does the depth perception. The cheapest 3D ever shipped.
How: wigglegram · motion parallax
Under the hood
The cloud rotates through a small oscillating angle. Motion parallax — near points moving more than far ones — is enough for your visual system to reconstruct depth. Stereo vision, single eye edition.
angle = Math.sin(t * 2.2) * 0.18; // the wiggle
// parallax does the rest, free of charge
The Horizon
A synthwave floor rolling toward an ember sun. Obligatory. The grid scrolls in perspective; the sun has scanlines because rules are rules.
How: perspective grid · scrolling rows
Under the hood
Horizontal lines are spaced by perspective (y ∝ 1/row) and scroll by recycling; vertical lines converge on the vanishing point. The sun is a gradient circle with gaps — the whole genre in 40 lines.
y = horizon + k / (row - scroll); // perspective
vertical: lerp(x, vanishX, depth);
sun: arc + scanline gaps
Kaleidoscope
Draw anything. Get it back eight ways, mirrored and perfect. This one also keeps your work — symmetry deserves persistence.
How: 8-fold mirror stamping
Under the hood
Every stroke point is stamped eight times: rotated into each 45° sector, alternating mirrored. The symmetry group does the artistry; you just have to move.
for (let k = 0; k < 8; k++) {
ctx.rotate(k * TAU / 8);
if (k % 2) ctx.scale(1, -1); // mirror
stamp(r, 0);
}
Droste Zoom
Frames inside frames inside frames, zooming forever. When the innermost frame reaches full size, it has silently become the outermost. You never see the loop close.
How: self-similar recycling
Under the hood
Each frame is the previous scaled by a constant ratio; all of them grow together and the largest is culled as it passes the viewport. An infinite zoom needs only six rectangles and a modulo.
scale *= 1 + 0.4 * dt;
if (scale > RATIO) scale /= RATIO; // the loop
draw frames at scale · ratioⁿ
The Torch
There's a message on this canvas. You get a torch beam the size of a biscuit. Reading is exploration now.
How: light mask compositing
Under the hood
The text is drawn, then covered in darkness except a radial gradient hole at your cursor (destination-out). Information architecture reduced to a flashlight — surprisingly tense.
drawText();
ctx.fillRect(0, 0, W, H); // darkness
cutHole(P.x, P.y, 70, 'destination-out');
Parallax Field
Five layers of dots, each tracking your cursor at a different fraction. Your brain reads depth into a flat rectangle instantly and refuses to stop.
How: layered offset tracking
Under the hood
Each layer offsets by cursor distance × its depth factor (0.02 to 0.14). Nearer layers move more — the monocular depth cue every scrolling site borrowed and few credit.
layer.x = P.x * layer.depth * 0.14;
// five layers, five speeds, instant depth
Wing VIII
The gallery
Generative art: the machine draws, you take the credit.
The Fractal Tree
A tree grown from one rule: split, shrink, tilt, repeat. Click to plant a different one. The wind is noise; the species is a random seed.
How: recursive branching · noise sway
Under the hood
Each branch spawns two children — shorter, tilted apart by a per-tree angle. Ten levels deep is 2 047 branches, swaying by noise sampled per depth so the canopy moves more than the trunk. Genetics is one random seed.
branch(len * 0.72, ang - spread + sway);
branch(len * 0.72, ang + spread + sway);
if (len < 4) leaf();
Coral Growth
Particles wander randomly until they touch the structure — then they stick, forever. Diffusion-limited aggregation: how coral, frost and lightning scars all build themselves.
How: DLA · random walkers
Under the hood
Walkers jitter randomly; contact with the frozen cluster freezes them too. Branches emerge because tips catch walkers before gaps do — growth mathematics with zero blueprint, maximum coral.
walker.jitter();
if (touchesCluster(walker)) freeze(walker);
// tips win: that's the branching
Circle Packing
Circles rain down and grow until they touch a neighbour, then stop. Watch the gaps fill with ever-smaller survivors. Restarts when it runs out of patience.
How: growth until contact
Under the hood
Random candidate points grow radius until touching any existing circle or the walls. Rejection plus growth fills space with the classic packing texture — the algorithm behind a thousand data-viz posters.
while (!touching(c)) c.r += 0.5;
if (c.r > MIN) circles.push(c);
else rejects++; // patience is finite
Truchet Drift
A grid of identical quarter-circle tiles, each randomly rotated — producing endless flowing labyrinths. Tiles flip themselves occasionally because permanence is boring.
How: quarter-arc tiles · random flips
Under the hood
Every tile holds two quarter-arcs in one of two orientations; edges always meet, so any random grid connects into mazes. A few tiles flip per second and locally rewire the entire labyrinth.
arc(0, 0, half) + arc(cell, cell, half)
// or the mirrored pair — edges always match
flip(randomTile(), every160ms);
Rose Curves
r = cos(kθ), the whole family, morphing through fractional k. Petal counts obey rules discovered before calculus had a name.
How: polar curves · morphing k
Under the hood
When k is an odd integer you get k petals, even integers give 2k, fractions bloom into overlapping laces. Here k drifts continuously so the rose never stops re-deciding what species it is.
r = R * Math.cos(k * theta);
x = r * cos(theta); y = r * sin(theta);
k += 0.04 * dt; // species drift
The Supershape
One formula, most of biology's silhouettes: starfish, diatoms, flowers, blobs. Six parameters drifting forever — taxonomy as animation.
How: superformula · parameter drift
Under the hood
Gielis' superformula generalises the circle with six knobs; small changes in m and the exponents jump between petals, polygons and organisms. The outline you're watching is one equation surfing its own parameter space.
r(θ) = (|cos(mθ/4)/a|^n2 +
|sin(mθ/4)/b|^n3)^(-1/n1)
// six numbers; most of biology's outlines
Strange Attractor
Half a million points through the same two lines of maths. Never repeats, never leaves, never stops being unreasonable to look at. De Jong's attractor, accumulating live.
How: iterated map · point accumulation
Under the hood
Each point feeds through x' = sin(a·y) − cos(b·x), y' = sin(c·x) − cos(d·y) and lands somewhere new on the same ghostly shape — chaotic orbits with additive glow, thousands per frame.
x1 = Math.sin(a * y) - Math.cos(b * x);
y1 = Math.sin(c * x) - Math.cos(d * y);
plot(x1, y1); // ×4000 per frame, forever
Night Skyline
A generated city that has never existed: three parallax layers, a thousand lit windows, aircraft warning lights. Click for a different city — infinite urban planning, zero traffic.
How: layered generation · window grid
Under the hood
Three building layers scroll at different speeds; windows are a random bitmask per building, lit amber with a small flicker chance. Every click reseeds the skyline generator — instant metropolis.
for (b of layer) windows.forEach(w =>
lit[w] = hash(seed, b, w) < 0.4);
layer.x -= speed[depth] * dt; // parallax
Invented Constellations
A star field, a random walk connecting the bright ones and a generated name. 'The Lesser Invoice.' 'Kaizen Ascending.' Click for a new prophecy.
How: random stars · name grammar
Under the hood
A handful of bright stars get connected by a greedy nearest-neighbour walk (the same way ancient astronomers freelanced), then a two-part name grammar christens it. Astrology, procedurally honest.
path = nearestNeighbourWalk(brightStars);
name = pick(adjectives) + ' ' + pick(nouns);
// as scientific as the originals
The finale
One hundred
You actually made it.
The Century
Exhibit one hundred. The seals rain, the counter agrees and the only remaining question is what your website could be if this much care went into it. You know where the button is.
How: confetti physics · a sincere pitch
Under the hood
One hundred K-seals with confetti physics, a counting ticker and the entire page's thesis in one sentence. The code is the same playroom gravity you met 97 exhibits ago — by now, an old friend.
if (exhibit === 100) {
rainSeals(100);
askForTheSale(); // politely
}
Seen enough?
One hundred experiments, zero libraries. Imagine this level of care pointed at your business instead of at showing off.