Three.js Scroll Prism — Spectrum Dispersion Fan

Three.js Scroll Prism Light Split · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Prism from CylinderGeometry with 3 radial segments — a zero-cost extruded equilateral triangle
Budget glass: MeshPhysicalMaterial at 22% opacity, near-zero roughness, plus an EdgesGeometry outline
Beams as stretched boxes via a layBeam(midpoint, length, atan2) helper — WebGL line-width limits bypassed
Physically ordered dispersion: per-ray bend factors from −3 to +3, violet deflecting most
White beam thins as the fan opens — energy visibly transferring into the spectrum
Two-phase scrub: beam arrival (30%), smoothstepped fan opening (70%)
Camera swings edge-on to frontal, reframing the subject from beam to spectrum
300 additive dust motes giving the light something to illuminate, plus independent per-ray shimmer

About this UI Snippet

How to Build a Scroll-Driven Prism Light Split With Three.js and GSAP

Screenshot of the Three.js Scroll Prism Light Split snippet rendered live

The Three.js Scroll Prism Light Split snippet stages the most famous experiment in optics: a white beam extends across a dark room into a glass prism, and as the user keeps scrolling, seven spectral rays fan out of the far face — red bending least, violet most — while the camera swings from an edge-on beam view to the full *Dark Side of the Moon* silhouette. GSAP's ScrollTrigger scrubs one dispersion value; the beams are boxes laid between computed points.

A prism from a three-sided cylinder

Three.js has no prism primitive, but CylinderGeometry(6, 6, 7, 3) — three radial segments — is exactly an extruded equilateral triangle. Rotated so its axis lies horizontal, it presents a triangular cross-section to the camera at zero modeling cost. The glass recipe repeats this series' budget formula (see the hourglass bulbs): MeshPhysicalMaterial at 22% opacity with near-zero roughness, depthWrite off, plus an EdgesGeometry wireframe overlay so the silhouette stays crisp even where the glass is nearly invisible.

Beams are boxes, not lines

WebGL line width is capped at 1px on most platforms, so glowing light rays can't be Line objects. Every beam here is a 1-unit BoxGeometry stretched between two points by a layBeam() helper: scale.x becomes the length, position the midpoint, rotation.z the atan2 of the direction. Additive blending makes overlapping beams brighten each other, and each ray's cross-section shimmers independently on clock time so the fan reads as *light*, not painted stripes. The white beam thins as dispersion grows — its energy visibly transferring into the spectrum.

Dispersion as a per-ray bend factor

Each of the seven rays stores a bend factor from −3 (red) to +3 (violet), and its exit angle is −0.12 − bend × 0.085 × dispersion — so at zero dispersion all rays overlap the white beam's line, and as the scrub advances the fan opens with violet deflecting most, the correct physics ordering. Ray length also grows with dispersion (4 to 50 units), shooting color to the frame edges at full split. Because angle and length are pure functions of the scrubbed value, scrolling back retracts the spectrum into the prism and restores the undivided white beam.

Two phases, one camera thesis

The scrub splits at 30%: first the white beam *reaches* — extending from off-screen to the entry face, an arrival that gives the split a cause — then the fan opens through a smoothstepped 70%. Meanwhile the camera swings 1.1 radians from nearly edge-on (where the beam is a bright line and the prism a dark triangle) to frontal (where the fan is widest). The camera move is doing argumentative work: it starts where the *beam* is the subject and ends where the *spectrum* is, the same choreography-as-explanation idea as the pendulum wave's profile-to-front slide.

Dust makes light visible

Beams in empty space are invisible in reality and unconvincing in 3D. Three hundred additive dust motes drift through the scene, giving the beams something to appear to illuminate — the same trick film sets use with haze machines. The prism itself rotates half a radian as dispersion grows, suggesting the split is *caused* by the geometry change, and idles with a gentle wobble so the scene never freezes. For the full-scene color treatment of this idea, see the scroll color morph; for beams at architectural scale, the lightning orb.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not need to fight WebGL's line-width limits or derive dispersion factors yourself. Paste this snippet's HTML, CSS, and JS into an AI assistant like Claude and ask it to explain the three-segment cylinder trick, the layBeam box-stretching helper, or why violet gets the largest bend factor. The same assistant can extend the optics — a second prism that recombines the fan back into white (Newton's actual follow-up experiment), a rainbow projection patch where the rays land, Snell's-law-accurate angles if you want the educational version, or the beam origin following the mouse before scroll takes over. It can also swap the seven spectral colors for your brand ramp with bend factors regenerated from array position. Treat the code as a starting point to interrogate and reshape, not a finished artifact.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "scroll-driven prism light split" in plain HTML, CSS, and JavaScript using Three.js and GSAP's ScrollTrigger plugin, all loaded from a CDN (no bundler, no build step).

Requirements:
- A pinned full-viewport section with a canvas, WebGLRenderer, PerspectiveCamera (resized with aspect on window resize), dim ambient plus one PointLight, near-black background.
- A prism from CylinderGeometry(6, 6, 7, 3) — three radial segments = extruded equilateral triangle — rotated so the axis is horizontal. Glass look: MeshPhysicalMaterial ~22% opacity, roughness ~0.02, DoubleSide, depthWrite false, plus an EdgesGeometry LineSegments outline.
- A layBeam(mesh, ax, ay, bx, by) helper that stretches a unit BoxGeometry between two points: scale.x = distance, position = midpoint, rotation.z = atan2 — because WebGL lines are capped at 1px. All beams use MeshBasicMaterial with AdditiveBlending and depthWrite false.
- One white beam and seven spectral rays (red 0xff3b30 → violet 0x9d4edc), each ray storing a bend factor from −3 (red) to +3 (violet).
- One GSAP tween (ease "none") scrubbing p 0→1 on a ScrollTrigger with pin: true, scrub ~0.5, end ~+=400%.
- Two derived phases: reach = min(1, p/0.3) extends the white beam from off-screen to the prism's entry face; fan = smoothstep((p − 0.3)/0.7) opens the spectrum. Each ray's exit angle = −0.12 − bend × 0.085 × fan, length = 4 + fan × 46, opacity = fan × 0.9 — violet must visibly deflect most. The white beam's cross-section thins by 55% as fan grows.
- ~300 additive dust motes drifting in the scene so beams appear to illuminate something; per-ray cross-section shimmer on clock time; prism rotates ~0.5 rad with fan plus an idle wobble.
- A camera swinging ~1.1 rad from edge-on to frontal across the scroll, lookAt slightly right of the prism; a DISPERSION % HUD from fan; intro overlay fades at p > 0.02.
- Confirm reverse scrolling retracts the spectrum into the prism and withdraws the beam.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Load the three CDN scriptsAdd three.min.js, gsap.min.js, and ScrollTrigger.min.js in that order before the snippet JS.
  2. 2
    Paste HTML, CSS, and JSA glass prism floats in a dark, dust-hazed room, viewed nearly edge-on — DISPERSION 0% in the HUD.
  3. 3
    Scroll to send the beamAcross the first 30% a white beam extends from off-screen and lands on the prism's entry face.
  4. 4
    Keep scrolling to split itSeven rays fan from the exit face — violet bending furthest — while the white beam visibly thins and the camera swings to the frontal view.
  5. 5
    Scroll backThe spectrum retracts into the prism and the beam withdraws — every angle and length is a pure function of the scrub.
  6. 6
    Restyle the spectrumEdit the SPECTRUM hex array (fewer, more, or brand colors) — bend factors derive from array position automatically.

Real-world uses

Common Use Cases

Creative-tool and brand-palette pages
Design tools whose product is color get their origin story: white in, spectrum out — swap SPECTRUM for your brand ramp.
Analytics and "one input, many insights" pitches
The prism is the diagram every BI deck draws — here it renders live, with rays as report categories annotated via scroll pin steps.
Physics and optics teaching pages
Correct dispersion ordering plus a scrub students can reverse — pair with the pendulum wave for a classroom set.
Music and album visuals
The Dark Side silhouette is a permanent cultural reference — land the full fan on a tracklist or tour date reveal.
Portfolio hero for colorists and photographers
Light-as-subject framing suits color graders; the dust-haze room sets a cinematic mood beyond a flat aurora background.
Puzzle games with light mechanics
Beam-routing puzzles can demo their core verb — split, bend, recombine — before a single screenshot.

Got questions?

Frequently Asked Questions

CylinderGeometry's radialSegments parameter controls the cross-section polygon: 32 gives a circle, 3 gives an equilateral triangle — which extruded along the axis is exactly a prism. Rotated 90° so the axis is horizontal, the triangle faces the camera. It ships in every Three.js build, needs no ExtrudeGeometry shape path, and its EdgesGeometry gives clean silhouette lines for the glass outline.

The WebGL spec only guarantees 1-pixel line width, and most platforms enforce it, so glowing beams cannot be drawn as lines. A unit BoxGeometry stretched by scale.x between two points — midpoint position, atan2 rotation — renders at any thickness, supports additive blending for overlap glow, and its cross-section can animate (the shimmer and the white beam's thinning) via scale.y/z.

The ordering is: violet carries the largest bend factor and deflects most, red least — matching real glass, where refractive index rises toward shorter wavelengths. The angles themselves are art-directed rather than Snell's-law-derived (a real 60° prism at this geometry would fan only a few degrees), a standard theatrical exaggeration so the split reads at page scale.

In reality a light beam crossing clean air is invisible — you see light only where it scatters off something. The 300 additive-blended motes drifting through the scene give the beams an atmosphere to visually illuminate, the same reason film sets use haze machines. Without them the boxes read as solid neon tubes; with them, as light through a dusty room.

Yes. Export via the JSX, Vue, Angular, or Tailwind buttons. Build the prism, beams, and ScrollTrigger inside a mount effect against a canvas ref; the layBeam helper and scratch vectors live in effect scope. On cleanup kill the ScrollTrigger, dispose the prism geometry, edges, every beam box's geometry/material, the mote system, and call renderer.dispose().