The Sprite
The story behind The Sprite
Inspired by Sprite (lightning) on Wikipedia
Built with Three.js · InstancedMesh · Custom ShaderMaterial · UnrealBloomPass
Techniques Procedural Recursive Branching · Instanced Cylinder Alignment · Depth-Dependent Color Gradient · Raycasted Click Positioning · Electrical Flicker
Direction You are the thunderstorm — each click triggers a millisecond ghost 80 km overhead
Result Massive branching columns of red-blue lightning that flicker into existence and fade before you can study them
The Story
Sprites are large-scale transient luminous events that occur above active thunderstorms at altitudes of 50 to 90 kilometres. Triggered by positive cloud-to-ground lightning, they last roughly five milliseconds. Airline pilots reported seeing them as early as the 1880s, but nobody believed them until the University of Minnesota accidentally captured one on camera in 1989 during an unrelated low-light test. They come in several morphologies — columns, jellyfish, and carrots — each with a bright red-orange trunk branching into blue-violet tendrils at the tips.
The concept maps directly: click the ground and something enormous erupts far above. The interaction is the phenomenon.
The Take
The screen is dark sky layered in a five-band atmosphere gradient, from near-black space at the top through deep mesospheric blue to a faint horizon glow at the bottom. A procedural cloud layer drifts slowly across the lower portion. Click anywhere and a sprite spawns above the corresponding position — a jagged central column with radiating head branches and tendrils sprouting along the spine. The sprite type is randomised between column, jellyfish, and carrot morphologies, each with different branch counts and depths.
Color follows a depth-dependent gradient through the branching tree: bright red-orange at the trunk (depth 0), shifting to pink and magenta through the mid-branches, ending in blue-violet at the finest tendrils. Each sprite lives for roughly three seconds, fading with an asymmetric envelope — sharp attack, slow decay. Narrative text surfaces at click milestones: “80 km above the storm,” then “sprite,” then the facts that make the phenomenon stranger.
The Tech
Instanced Cylinder Segments with Custom Vertex Alignment
Each sprite is decomposed into individual line segments — a central column of 5 to 9 jagged spine segments plus all recursive branches. Every segment is rendered as a four-sided cylinder instance via a single Three.js InstancedMesh (capacity 8,000 segments). The custom vertex shader handles alignment: given aStart and aEnd attributes per instance, it computes the midpoint, direction axis, and constructs a local coordinate frame using cross products. The cylinder geometry is scaled along Y by the segment length and along XZ by a per-instance thickness attribute, then rotated into the computed frame. No matrix uploads — the alignment is entirely vertex-shader math.
Procedural Recursive Branching
Sprites are generated on CPU via a recursive branch() function. At each depth level, segment length decreases, spread angle widens, and branch probability drops. The trunk uses a separate multi-segment jagged column with per-segment XZ jitter to simulate the erratic path of electrical discharge. Head branches radiate outward from the top at angles determined by the sprite type — jellyfish morphology produces 6 to 10 wide-spread branches, carrot produces 4 to 7 tighter ones, column produces 3 to 5. Additional tendrils spawn from random points along the central column at deeper initial depth, producing finer structure.
Depth-Dependent Color and Electrical Flicker
Color is computed per segment based on branching depth ratio. Trunk segments (depth ratio < 0.3) are bright red-orange. Mid-branches (0.3 to 0.6) transition to red-magenta with increasing blue. Tip tendrils (> 0.6) shift to blue-violet. The vertex shader adds electrical flicker by modulating alpha with two layered sine functions at different frequencies (40 Hz and 17 Hz), seeded per instance via a random aSeed attribute. Combined with the fast fade-in (60 ms) and slow fade-out (650 ms to end of life), this produces the characteristic rapid strobing of real sprite observations.
Raycasted Click Positioning and Post-Processing
Click position maps to 3D world coordinates via a Three.js Raycaster intersecting a plane at z = -15. This places the sprite’s origin above the click in the scene’s coordinate space, with slight random offset for natural variation. The rendering pipeline uses ReinhardToneMapping (exposure 1.3) feeding into an EffectComposer with UnrealBloomPass (strength 1.4, radius 0.7, threshold 0.2). Additive blending on the sprite material ensures overlapping segments accumulate brightness, and the bloom pass picks up the hot cores of each column to produce the characteristic atmospheric glow. A fullscreen atmosphere gradient shader renders behind everything with a subtle Gaussian horizon glow.
This blog post was AI generated with Claude Code. Authored by Artificial Noodles.