The Cherenkov
The story behind The Cherenkov
Inspired by Cherenkov radiation on Wikipedia
Built with Three.js · Orthographic screen-space camera · Custom cone envelope geometry · EffectComposer (Afterimage + Bloom + Vignette)
Techniques Trail ring buffer · Per-sample photon emission · cos θ = 1/(nβ) envelope reconstruction · Additive cobalt shader · Age-based cone fade
Direction Move faster than light in the medium. Leave a shockwave behind you.
Result A cobalt cone of photons that opens behind your cursor the instant you cross β > 1/n, brightens as you accelerate, and leaves a trailing afterglow across the dark water
The Story
In 1934, Pavel Cherenkov was a graduate student in Sergei Vavilov’s lab in Moscow, working through a long list of fluorescent liquids that nobody else wanted to measure. His job was to bombard them with gamma rays from a radium source and watch for the faint glow of luminescence. Most of the liquids behaved as expected. Pure water did not. In a completely dark room, after an hour of dark-adapting his eyes, Cherenkov saw a faint, uniform blue light coming from the water itself — from any transparent liquid in fact. It had no spectral signature of a known fluorescent dye, it was polarized, it pointed forward, and it refused to go away no matter how much he purified the sample.
It took three more years and Igor Tamm and Ilya Frank to explain it. A charged particle moving through a dielectric medium polarizes the atoms along its path. If the particle is moving faster than the phase velocity of light in that medium — not faster than c, but faster than c/n, where n is the refractive index — then the polarization wake can’t keep up. The re-radiated photons pile into a coherent shockwave, exactly analogous to a sonic boom from a supersonic jet, except made of light instead of sound. The half-angle of the cone depends only on how fast the particle is going: cos θ = 1 / (nβ).
Cherenkov, Tamm, and Frank shared the 1958 Nobel Prize for this. It’s the reason a nuclear reactor pool glows blue. Every time a beta particle shoots out of the fuel faster than light can move through water, another cone of photons gets added to the glow. You are looking at a continuous shower of photonic sonic booms.
This experience puts your cursor in the center of that shower.
The Take
You are a relativistic electron swimming through heavy water. The dust specks scattered across the background are water molecules. The cobalt-rimmed point that follows your cursor is you.
Move slowly and nothing happens. You are slower than light in the medium, the polarization wake catches up with you, and the photons cancel each other out. The HUD reads β 0.000 c, θ_c —, photons zero.
Push past a certain velocity and the world changes. Your β number crosses 0.752 (that’s 1/n for water) and the readout turns cyan. Behind you a cone opens — not drawn in as an effect, but reconstructed from physics. Every past position of your cursor still has photons in flight, and those photons have traveled outward at c/n in the Cherenkov emission direction. The cone is the set of those photons as they are now. The faster you were going at the moment you passed through a point, the tighter the cone was at that point. Look closely at the cone wall and you can see it narrowing where you were fastest and flaring where you were slower.
As you accumulate photons, marginalia appears: Light slows in water → Move faster than its local speed → Photons you already emitted are pinned behind you → cos θ = 1 / (nβ) → Pavel Черенков, 1934 → This is the blue a reactor core glows with. You are the particle.
The reveal at the end is the whole point. Every reactor pool photograph, every stock image of “nuclear blue,” every glowing rod submerged in a lab — that specific color isn’t a paint job. It’s geometry. It’s the solution to cos θ = 1/(nβ) for an electron moving through water at relativistic speed. You just spent sixty seconds drawing it with your hand.
The Tech
Screen-Space Orthographic Camera
The scene is 2D. Three.js is used purely for its renderer, shaders, and post-processing pipeline — not for its scene graph. An OrthographicCamera is configured so that (0, 0) sits at the top-left of the canvas and (W, H) at the bottom-right, mirroring DOM coordinates exactly. This means pointer events and simulation math can work in raw pixel space without any projection math. The entire experience is drawn on a single flat plane at z=0, which keeps the cone-envelope geometry trivial to reason about.
The Trail Ring Buffer
Every animation frame, the current pointer state is captured as a sample:
{ x, y, vx, vy, v, t }
— position, velocity vector, speed, and timestamp. Samples are pushed into a circular ring buffer of length 90, with old samples evicted whenever t_now − t exceeds TRAIL_MAX_AGE (0.85 seconds). This is the physics memory of the particle: every position the electron passed through, and how fast it was going when it did.
Cherenkov Envelope Reconstruction
The cone is not a static mesh. Every frame, it is rebuilt from scratch by walking the trail backward and computing, for each sample, the position of the photons that were emitted at that instant.
For a sample with velocity v and normalized motion direction (dx, dy), the Cherenkov emission directions are:
θ_c = acos(1 / (nβ)) // half-angle from the motion axis
e_L = cos(θ_c) · (dx, dy) + sin(θ_c) · (−dy, dx) // left emitter
e_R = cos(θ_c) · (dx, dy) − sin(θ_c) · (−dy, dx) // right emitter
The photons travel at c/n. So the time-of-flight since emission is dt = t_now − sample.t, and each photon is now at:
L = sample.pos + e_L · (c/n) · dt
R = sample.pos + e_R · (c/n) · dt
Connecting the L points across consecutive samples yields the left edge of the cone. Connecting the R points yields the right edge. Filling the triangle strip between them and the emitter position yields the interior. This is all reconstructed into preallocated BufferGeometry position arrays each frame — no garbage, no allocations. Samples with β below 1/n are skipped entirely (there’s no Cherenkov cone for them).
This is the single most important trick in the whole experience. The cone is not faked. It is geometry derived from the actual physics, one sample at a time.
Sub-Threshold Gating
β < 1/n means the emission angle is undefined — there is no cone. The code checks this per sample and skips it, which naturally produces the correct behavior: as you slow down past the threshold, pieces of the cone simply stop being drawn. As you speed up through the threshold, they begin appearing. The edge of the cone visibly opens and closes in response to your motion, which is exactly what the physics predicts.
The Fill and Edge Shaders
Three geometries render the cone: a triangle-strip fill (coneFill) and two Line objects for the crisp left and right edges. All three are additively blended. The fill uses a cobalt-to-cyan gradient based on alpha, with a soft central brightening along the strip’s midline to suggest volume. The edges use a brighter shader that pumps saturation at the leading front of the cone — the newest samples get the brightest photons, because that’s where the shockwave is freshly forming.
The fill shader takes an alpha attribute per vertex, which is computed from two factors: the age of the sample relative to TRAIL_MAX_AGE (younger = brighter), and how far β is above the threshold (faster = brighter). A vertex from a slow sample will dim toward black; a vertex from a very fast sample will saturate toward full cobalt.
Dust Field as Medium
5,200 point sprites (2,400 on mobile) are scattered across the canvas. They are rendered as a custom ShaderMaterial that flickers each point between two brightness levels on an independent sine phase. There is no physics to them — they exist purely so that your eye has a reference frame for the water. Without them the cursor would feel like it’s moving through empty space, not a medium with a refractive index. With them, you can see the medium even though it’s transparent.
Post-Processing Pipeline
RenderPass
→ AfterimagePass (damp 0.94)
→ UnrealBloomPass (strength 1.1, radius 0.82, threshold 0.18)
→ Custom vignette ShaderPass
→ screen
The AfterimagePass with damp 0.94 creates a persistence trail — each pixel retains 94% of its previous frame’s contribution, so the cone’s afterglow lingers long after the electron has moved on. This is what gives the slow decay of residual photons. The UnrealBloomPass with a low threshold (0.18) pulls out the cobalt-saturated regions and blooms them into a reactor-pool haze. The vignette pass darkens the corners to draw attention inward.
The bloom threshold is deliberately low because Cherenkov radiation is essentially monochromatic blue, and the cobalt shader output sits right at the threshold. Bloom radius is set high (0.82) because the cone is long and thin — we want the glow to spread axially, not just pool around bright points.
Idle Demo
If no pointer input arrives within ~1.6 seconds of load, an auto-animation takes over: the pointer moves along a Lissajous arc around the viewport center, with a velocity envelope that accelerates past the Cherenkov threshold and decelerates back below it. This means the cone opens, closes, and reopens on its own until the user starts interacting. The experience is never dead on arrival.
Mobile
Touch events are wired the same way as mouse events. Dust count drops from 5,200 to 2,400, trail length stays at 90 (it’s already small), and the HUD scales down via CSS media queries. The cone geometry math doesn’t change — only the particle budget.
This blog post was AI generated with Claude Code. Authored by Artificial Noodles.