The Cascade

Artificial Noodles ·

Inspired by Plasma recombination on Wikipedia

Built with Three.js r160 · Custom ShaderMaterial · Points Geometry · EffectComposer Bloom · Canvas Text Rasterization

Techniques Discrete energy level state machine · Spectral color mapping (Balmer series) · Perspective-correct point sizing · Canvas-to-world text sampling · Flash particle pool · Cursor re-ionization field · Per-particle orbital motion

Direction The most beautiful light in physics is emitted when things calm down. Recombination light is the signature of settling.

Result Text that doesn’t appear — it precipitates. 15,000 particles cascade through quantum energy levels, emitting spectral flashes at each transition, until they settle into the words FALLING INTO PLACE

The Story

380,000 years after the Big Bang, the universe was still opaque. A fog of free electrons scattered every photon the instant it was emitted. Light could not travel. The cosmos was white noise.

Then the temperature dropped below 3,000 Kelvin, and something extraordinary happened. Free electrons, which had been too energetic to be captured, began falling into the orbital shells of hydrogen nuclei. Each capture was a cascade: the electron did not simply appear in the ground state. It fell through a series of discrete energy levels — from the sixth shell to the fifth, from the fifth to the fourth — emitting a photon at each step. The frequency of each photon was determined by the energy gap between levels, producing the hydrogen spectral series: Lyman ultraviolet, Balmer visible, Paschen infrared.

This process — plasma recombination — happened everywhere, simultaneously, across the entire observable universe. The fog cleared. Light traveled freely for the first time. The cosmic microwave background radiation that fills the sky today is the afterglow of that moment: the oldest light in the universe, emitted when electrons finally settled down.

The physics is counterintuitive. The light was not produced by excitation — not by heating, colliding, or energizing. It was produced by de-excitation. By cooling. By settling. The most information-rich light in cosmological history was emitted by particles falling into place.


The Take

The experience opens with particles spawning at the highest energy level — a violet fog of orbital chaos. Each particle traces a small orbit around an invisible home position, jittering with the restless energy of a free electron. The home positions are not yet meaningful. They are coordinates sampled from text, but the text does not exist yet. It is latent.

Over several seconds, the particles begin to cascade. One by one, their energy state drops: violet to blue, blue to cyan, cyan to red, red to amber. At each transition, a flash particle fires — a brief bright burst at the spectral color of the level being left behind. The particle’s orbit tightens. Its color warms. It slows.

When a particle reaches the ground state, it settles. The orbit contracts to nearly zero. The color becomes warm silver. And because 15,000 particles are all settling into positions sampled from the same text, a word materializes: FALLING. Not drawn, not faded in — precipitated from a cooling plasma.

Then INTO cascades into view below. Then PLACE. The phrase completes itself: FALLING INTO PLACE. The text describes exactly what the physics just did. The concept is self-referential — the medium enacts its own message.

The viewer’s cursor acts as a heat source. Moving near settled particles re-ionizes them — kicking them back to higher energy levels, restoring orbital chaos, stripping the text apart. Pull the cursor away, and the cascade begins again. The particles fall back through the spectral sequence and reassemble. The text is never permanent. It exists only as the equilibrium state of a system that can always be disturbed.

On mobile, the same interaction works through touch. An auto-advance timer ensures the full phrase reveals even without interaction — each word cascading in after eight seconds of stillness.


The Tech

Particle System: 15,000 Points with Custom ShaderMaterial

The particle system uses Three.js Points geometry with a custom ShaderMaterial. Each particle has six custom attributes stored in typed arrays: position (vec3), color (vec3), size (float), and alpha (float). The position and color arrays use DynamicDrawUsage since they update every frame; size updates less frequently.

The vertex shader computes perspective-correct point sizes:

gl_PointSize = max(1.0, aSize * uPR * uRefDist / max(1.0, -mv.z));

where uPR is the device pixel ratio and uRefDist is a reference distance constant. This ensures particles maintain consistent apparent size regardless of camera distance, while still exhibiting depth-based scaling. The fragment shader uses a smoothstep(1.0, 0.3, d) falloff from center to edge, producing particles with bright cores and soft halos — sharper than a Gaussian but smoother than a hard circle.

Base particle sizes are 3px on desktop and 4px on mobile, with an additional 0.8px per energy level — higher energy particles appear slightly larger, matching the physical intuition that excited states occupy more space.

Energy Level State Machine

Each particle maintains an integer energy level (0–5) and a continuous timer. The cascade is driven by a per-particle countdown: when the timer expires, the energy level decrements by one. Transition times follow a stochastic schedule — 1.2 + Math.random() * 1.8 seconds per level — ensuring particles cascade at different rates, producing the visual effect of a gradual settling rather than a synchronized snap.

At each level transition, the particle’s target color updates to the spectral color for the new level. The mapping follows a simplified hydrogen emission spectrum:

LevelColorPhysical Analogue
5Violet (#8b5cf6)Balmer-delta
4Blue (#3b82f6)Balmer-gamma
3Cyan (#06b6d4)Balmer-beta
2Red (#ef4444)Balmer-alpha
1Amber (#f59e0b)Paschen transition
0Warm silver (#e8ddd0)Ground state

Actual particle colors interpolate smoothly between levels using THREE.Color.lerpColors, so transitions produce continuous spectral gradients rather than discrete jumps.

Orbital Motion

Particles in excited states orbit their home positions with per-particle phase, radius, and speed parameters. The orbit radius scales with energy level — higher levels produce wider orbits. The motion is computed as:

const orbitR = level * 0.04 * jitter;
const angle = phase + elapsed * orbitSpeed;
x = homeX + Math.cos(angle) * orbitR;
y = homeY + Math.sin(angle) * orbitR * 0.6;
z = homeZ + Math.sin(angle * 0.7) * orbitR * 0.3;

The y and z scaling factors break the orbit out of a flat circle into an elliptical, slightly precessing path that reads as three-dimensional even on a 2D screen. At ground state (level 0), the orbit radius drops to near zero and the particle settles precisely onto its text-sampled home position.

Flash Particle Pool

Level transitions emit flash particles — brief bright bursts at the spectral color of the departed level. A pool of 300 flash particles is pre-allocated with its own Points geometry and ShaderMaterial using AdditiveBlending. When a main particle transitions, the next available flash particle is activated at the transition position with the departed level’s color.

Flash particles have a 0.4-second lifetime. Their alpha decays linearly, and their size scales with alpha: 10.0 * aAlpha pixels at reference distance. The color output is multiplied by 1.5 to push flashes above the bloom threshold, creating a brief spectral glow at each transition. The flash system was the source of a critical rendering bug during development — an earlier formula produced 1250px point sizes that white-washed the entire screen.

Canvas Text Rasterization to World Positions

The text “FALLING INTO PLACE” is rasterized on a hidden canvas using Archivo Black at a responsive font size (max(28, min(window.width * 0.12, 140)) pixels). The canvas is scanned pixel-by-pixel; any pixel above an alpha threshold of 128 becomes a candidate home position.

A global scale factor ensures all three words use the same pixel-to-world conversion, computed from the widest word’s measured width:

const maxW = Math.max(...WORDS.map(w => ctx.measureText(w).width));
_globalScale = _textWorldWidth / maxW;

This prevents shorter words (INTO) from scaling up to fill the same width as longer words (FALLING), which would make their characters disproportionately tall. Particles are distributed across candidate pixels using uniform stride sampling: step = Math.ceil(onPixels.length / budget), ensuring coverage across the entire text rather than concentrating in the left half.

On mobile, _textWorldWidth is computed from the camera’s visible viewport width to prevent text from exceeding screen bounds:

const visibleWidth = 2 * CAMERA_DIST * Math.tan(halfFov) * aspect;
_textWorldWidth = Math.min(4.0, visibleWidth * 0.82);

Re-Ionization: Cursor as Heat Source

The cursor position is projected into 3D world space and compared against each ground-state particle’s position. Particles within the ionization radius (0.5 world units) are kicked back to a higher energy level proportional to their proximity — closer particles receive more energy. The re-ionized particle restarts its cascade timer, its orbit expands, and it begins the spectral descent again.

This creates a visual effect where moving the cursor through settled text disrupts it — particles erupt into colored orbital chaos, then slowly re-cascade back to silver equilibrium. The interaction is reversible and non-destructive: remove the cursor, and the text reforms. The system always tends toward its ground state.

Post-Processing: Bloom, Vignette, Film Grain

The scene renders through a Three.js EffectComposer with three passes. UnrealBloomPass at strength 0.8, radius 0.5, threshold 0.35 adds glow to the brighter flash particles and high-energy spectral colors without washing out the settled silver text. A custom vignette ShaderPass darkens the edges with a tunable offset and darkness. A film grain pass adds animated noise at low intensity, preventing the dark background from reading as flat digital black.

Word Progression and Narrative Arc

The experience progresses through five phases: genesis (particles spawn at max energy), first cascade (FALLING materializes), interactive pause (hint text appears, cursor interaction enabled), progressive reveal (INTO and PLACE cascade in sequence), and sandbox (full phrase, continuous interaction). The progression is time-driven with interaction gates — each new word begins cascading either when the previous word has mostly settled or after an 8-second timeout, whichever comes first. On mobile, the timeout ensures the full experience plays out even without touch input.


Experience: The Cascade


This blog post was AI generated with Claude Code. Authored by Artificial Noodles.