The Grid
Inspired by Neon color spreading on Wikipedia
Built with Pure WebGL2 · Custom GLSL Fragment Shader
Techniques Ehrenstein radial figures · Cursor trail activation · Color cycling · Anti-aliased line rendering · Perceptual illusion (no post-processing)
Direction The most vivid thing on screen was never drawn — your visual cortex invented it
Result A field of radial line figures that bloom with color near your cursor, tricking your brain into seeing luminous discs in every gap. Press space to prove they don’t exist.
The Story
In 1971, Dario Varin showed that if you take an Ehrenstein figure — a set of radial lines with a gap at the center — and color the inner segments, something strange happens. Viewers perceive a glowing, translucent disc floating in the gap. Vivid. Luminous. Unmistakably there.
Except it isn’t there. There is no disc. No circle. No gradient. No fill. The gap region contains exactly the same white pixels as the background. Your visual cortex, confronted with colored line endings that form a circular boundary, does what it always does: it completes the shape. It fills in what “should” be there based on the surrounding context. And what it fills in isn’t subtle — it’s a bright, neon-glowing surface that appears to hover in front of the lines.
Pim van Tuijl named the phenomenon “neon color spreading” in 1975. The mechanism involves end-stopped neurons in V1 and V2 — cells that detect where lines terminate — feeding into the brain’s contour completion system. The visual cortex detects the circular arrangement of line endings, infers a boundary, and floods the interior with interpolated color. It’s not a vague impression. It’s a confident, saturated hallucination manufactured by your own perceptual machinery, applied to empty white space.
The most unsettling part: knowing the disc isn’t there doesn’t make it disappear. You can understand the illusion completely and still see it.
The Take
The experience renders a field of Ehrenstein figures on white — black radial lines, twelve per figure, each with a central gap. The grid breathes at 0.3 Hz, line endpoints oscillating by fractions of a pixel. Clinical. Precise. Still.
Move your cursor and the inner segments of nearby figures bloom with color. Violet, then teal, then rose — cycling slowly based on elapsed time. The color follows your cursor in a trailing wake, 24 positions deep, each decaying over about two seconds. Where the colored segments form rings around the central gaps, the neon spreading illusion fires. Phantom discs appear — luminous, hovering, seemingly real. They aren’t.
The shader never draws a circle. Never fills a gap. Never places a single colored pixel inside the white region bounded by the line endings. The fragment shader computes radial line geometry per cell, applies color to the inner half of each line based on cursor trail proximity, and outputs white everywhere else. The phantoms exist only in the space between your retina and your visual cortex.
Press space. All color is stripped — uReveal goes to 1.0, multiplying activation to zero. The lines revert to pure black. The phantoms die instantly. The gap was always empty. Release, and the illusion snaps back. Your brain resumes lying to you, immediately and without hesitation.
The Tech
Pure WebGL2 — single fullscreen quad. No Three.js, no FBOs, no textures, no multi-pass. One vertex shader, one fragment shader, one draw call. The entire visual is computed per-pixel.
Ehrenstein Grid Geometry. The shader tiles the viewport into cells, each centered on an Ehrenstein figure. For each pixel, it computes the local position relative to the nearest cell center, then tests against 12 radial lines using perpendicular distance: abs(local.x * lineDir.y - local.y * lineDir.x). Lines extend from an inner radius (the illusion gap) to an outer radius (88% of half-cell). Any pixel not on a line is output as white and the shader returns early — this is the conceptual constraint. The gap region is never touched.
Cursor Trail Activation. The CPU maintains a ring buffer of 24 recent cursor positions, uploaded each frame as uniform arrays (uTrail[24], uTrailAge[24]). The shader iterates over trail points, computing distance from each point to the current cell center in pixel space. Activation falls off with both distance (a radius that shrinks from 320px to 140px as the trail point ages) and time (smoothstepped decay over 2.5 seconds). The strongest activation across all trail points wins. This produces the spreading, fading wake of color that follows the cursor.
Color Cycling. The active color is determined by the time at which the winning trail point was recorded: colorPhase = (elapsedTime - trailAge) / 20.0. This maps through a three-stop gradient — violet (0.45, 0.15, 0.95) to teal (0.0, 0.65, 0.85) to rose (0.92, 0.08, 0.30) — via piecewise linear interpolation. Because older trail points have different timestamps, the wake naturally shows a gradient of recent color history.
Inner Segment Coloring. Not the entire line gets colored — only the inner 50%. A smoothstep from the midpoint to the inner radius creates a gradient that concentrates color near the gap boundary. This is what triggers the neon spreading: the brain needs colored line endings forming a circular arrangement, not uniformly colored lines. The colored portions also render slightly thicker (2.8px half-width vs 1.8px for inactive lines), subtly emphasizing the active segments.
Breathing. A slow sinusoidal oscillation (sin(time * 0.3 * 2π)) modulates the inner gap radius by 0.6 pixels. Imperceptible consciously, but it prevents the grid from feeling dead. Respects prefers-reduced-motion — the illusion works in still images, so the experience is fully functional without animation.
Idle Auto-Animation. If the cursor is inactive for 5 seconds, a slow circular drift (sin/cos at 0.4 rad/s) takes over, smoothly blended in over 2 seconds. The grid is never static on first load — the idle path activates figures in a gentle orbit, ensuring visitors see the illusion before they interact.
Mobile. Touch events map to the same trail system. Long-press (600ms) triggers the reveal mode instead of spacebar. Cell size scales down to max(80, minDim / 4.5) on small screens, keeping figures large enough for the illusion to work while fitting enough on screen to see the spreading pattern.
This blog post was AI generated with Claude Code. Authored by Artificial Noodles.