The Reflecting

Artificial Noodles ·

Inspired by Reflecting instrument on Wikipedia

Built with Three.js · EffectComposer · Custom Gravitational Lens ShaderPass · UnrealBloomPass

Techniques Point-mass lens equation · Einstein ring rendering · Chromatic aberration via ring-proximity gaussian · Power-law star distribution · Additive bloom

Direction A sextant measures where stars appear — but gravity decides where they actually are

Result A star field you warp with your cursor, bending light into luminous rings that grow as your gravitational mass accumulates

The Story

A reflecting instrument — sextant, octant, quintant — works by double reflection. Two mirrors fold light so that a navigator can see two celestial bodies simultaneously, superimposed in a single eyepiece. Measure the angle between them and you know where you are on Earth. For three centuries this was the most precise instrument at sea. Its one assumption: that starlight travels in straight lines.

In 1915, Einstein showed that it doesn’t. Mass warps spacetime, and light follows the curvature. A star near the edge of the sun appears slightly displaced — pushed outward from where Newtonian mechanics predicts. Arthur Eddington confirmed this in 1919 during a solar eclipse, measuring star positions that were wrong by exactly the amount general relativity required. Every sextant reading ever taken was slightly off. The instrument was perfect. The universe was curved.

The deepest version of this effect produces an Einstein ring: when a massive object sits precisely between you and a distant light source, the light bends equally in all directions, smeared into a perfect circle around the lens. The source is behind the mass. You see it everywhere around it.


The Take

Your cursor is a gravitational mass dropped into a field of 8,000 stars. As you move, starlight bends around your position — distorted, stretched, magnified. Stars near your cursor are deflected outward. Stars at a critical distance form arcs. When the geometry aligns, a full Einstein ring appears: a luminous halo of warped starlight encircling your cursor.

The mass isn’t instant. It accumulates. The longer you interact, the more solar masses your gravitational lens acquires — displayed in the top-right as a running counter in M☉. At 0.5 solar masses, the first narrative text appears: “A reflecting instrument measures the angle between two stars.” By 50 solar masses, the ring dominates the screen and the final text arrives: “The universe watches itself through bent light.”

The creative connection to the reflecting instrument is the inversion. A sextant assumes geometry is flat and measures angles precisely within that assumption. This experience shows what happens when geometry itself is bent — the measurement becomes the illusion, and the distortion becomes the truth.


The Tech

The Lens Equation: Point-Mass Deflection

The gravitational lens runs as a custom ShaderPass in the EffectComposer pipeline, operating on the rendered star field as a full-screen post-processing effect. The core math is the thin-lens point-mass equation:

β = θ - θ_E² / θ

Where β is the true source position, θ is the apparent position, and θ_E is the Einstein radius. In the shader, for each pixel at distance r from the cursor (in aspect-corrected UV space), the deflection is eR² / r. The source UV is computed as: lens center + direction × (r - deflection × strength). Pixels near the Einstein radius get pulled hardest — their source coordinates wrap around the lens, sampling star light from behind the mass. This creates the ring.

The Einstein radius itself grows logarithmically with accumulated interaction time: min(0.18, log(1 + totalMass × 0.5) × 0.06). This caps the ring at roughly 18% of viewport height but makes the first few seconds of interaction produce the most dramatic growth. The strength parameter ramps separately, reaching full intensity at about 6.7 solar masses.

Chromatic Aberration at the Ring Edge

Standard chromatic aberration spreads color separation uniformly across the screen. That’s wrong for gravitational lensing — the dispersion should be concentrated where the deflection is strongest, at the Einstein ring itself.

The shader computes ring proximity as a Gaussian: exp(-(r - eR)² / (eR² × 0.08)). This peaks sharply at the ring radius and falls off rapidly on both sides. Chromatic spread is then 0.03 × eR × strength × ringProximity — added to the red channel’s deflection, subtracted from blue, green unchanged. The result: stars far from the ring appear normal white, but arcs forming the ring itself bleed into spectral fringes. Red bends less, blue bends more, exactly as wavelength-dependent refraction would behave.

Star Field: Two Populations

The main field is 8,000 points (4,000 on mobile) distributed in a sphere of radius 15–100 units, offset 50 units behind the camera. Star colors are drawn from a palette of six — blue-white, white, warm white, gold, orange, and red giant — with a 70% bias toward the cooler end. Size follows a power-law distribution (random³ × 3.0 + 0.3), producing many dim points and a few bright ones, matching real stellar luminosity distributions.

A second population of 60 bright accent stars (30 on mobile) sits closer, at radius 10–40 units, with point sizes of 4–10. These exist specifically for the lens — when a bright star passes near the Einstein radius, it stretches into a dramatic visible arc. The main field provides the diffuse glow; the accent stars provide the landmark moments.

Both populations use a shared ShaderMaterial with additive blending and a soft radial falloff (pow(1.0 - smoothstep(0, 1, dist), 1.5)), rendering each star as a soft circle rather than a hard pixel.

Post-Processing Chain

Four passes in sequence:

  1. RenderPass — renders the point-sprite star field against #06060f deep navy.
  2. Gravitational Lens ShaderPass — applies the point-mass deflection with chromatic aberration. Receives cursor position, Einstein radius, strength, and aspect ratio as uniforms.
  3. UnrealBloomPass — threshold 0.35, strength 0.7 at rest, rising to 1.2 with accumulated mass (0.7 + min(0.5, totalMass × 0.015)). The low threshold ensures that even moderately bright stars contribute to the glow, while mass accumulation makes the entire field progressively more luminous.
  4. Vignette ShaderPass — darkens edges with 1.0 - dot(uv - 0.5, uv - 0.5) × 0.7, clamped to 0.3 minimum. Adds a subtle blue tint in shadows via mix(rgb, rgb × vec3(0.9, 0.92, 1.05), 0.3), pushing the dark navy toward a cooler tone.

Tone mapping is set to NoToneMapping — the star field is already in a controlled brightness range, and any tone mapping curve would dull the additive bloom accumulation that makes the Einstein ring glow.

Magnification Brightening

Real gravitational lensing conserves photons but concentrates them — objects near the Einstein ring appear magnified and brighter. The lens shader adds a magnification term: 1.0 + smoothstep(eR × 0.25, 0.0, |r - eR|) × 0.8 × strength. Stars within 25% of the ring radius get up to 80% brighter. On top of this, a faint ring glow (exp(-ringDist² / (eR² × 0.015)) × 0.12) in silver-blue adds a subtle luminous edge to the ring itself — the visual signature that something invisible is bending the light.

Interaction and Narrative

Cursor position is smoothed with lerp(current, target, min(1, dt × 8)) — responsive but not jittery. Mass accumulates at 2 M☉ per second of interaction. Five narrative milestones trigger at 0.5, 3, 8, 20, and 50 M☉, each displaying centered italic text in Source Serif 4 that fades in over 1.4 seconds, holds for 6 seconds, then fades out. The text sits at the bottom center of the viewport, above the HUD safe zone.

On mobile, touch events (start, move) map to the same lens position. The star count halves and accent stars drop to 30, but the lens shader is unchanged — the physics works identically on touch.


Experience: The Reflecting


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