Three.js Scroll Tunnel — GSAP ScrollTrigger WebGL Flythrough

Three.js Scroll Tunnel Travel · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

One shared CatmullRomCurve3 defines both the tunnel geometry and the camera path — always perfectly centered
Single scrubbed parameter: GSAP tweens one 0–1 value that walks the camera along the curve each frame
Look-ahead targeting: the camera faces a point sampled further down the curve, so it banks into every turn
Forty hue-swept neon rings thread the path to convey genuine forward speed and depth
FogExp2 matched to the page background hides the tube end and blends the canvas into surrounding sections
Smoothed scrub (0.7): camera travel glides toward scroll position instead of snapping frame-perfectly
Live depth HUD derived from curve.getLength() times the scrubbed parameter
Fully reversible and pinned — scrolling back up replays the descent backward with no extra code

About this UI Snippet

How to Build a Scroll-Driven Neon Tunnel Flythrough With Three.js and GSAP

Screenshot of the Three.js Scroll Tunnel Travel snippet rendered live

The Three.js Scroll Tunnel snippet drops the visitor inside a glowing wireframe tube and flies them down its length as they scroll — the scrollbar drives camera position directly along a curved 3D path, not a timer — by pairing Three.js TubeGeometry with GSAP's ScrollTrigger plugin, both loaded from a CDN.

One curve defines both the tunnel and the camera path

The whole effect hinges on a single CatmullRomCurve3 built from a dozen control points that gently wander in X and Y while marching steadily in negative Z. That one curve is used twice: once to extrude the TubeGeometry that becomes the visible tunnel walls, and again as the exact path the camera travels. Because the geometry and the camera share the same curve, the camera is always perfectly centered inside the tube looking down its throat — there is no separate "align the camera to the wall" step to get wrong.

A single scrubbed number walks the whole scene

Rather than tweening the camera object, the snippet scrubs one plain value — travel.t — from 0 to just under 1. Every frame, curve.getPointAt(t) returns the camera's exact world position and curve.getPointAt(t + 0.01) returns a look-ahead point a little further down the tube. Deriving everything from one normalized parameter means the tunnel can be reshaped, lengthened, or curved differently without touching the ScrollTrigger timeline at all.

Look-ahead, not look-at-center

If the camera simply stared at the tunnel's far end, cornering would feel wrong — you would see the wall swing past rather than the path bend toward you. Instead the look target is sampled a small fraction of the curve *ahead* of the camera's current position, so the camera always faces the direction of travel and banks naturally into each turn, exactly like a real vehicle following a track.

Neon rings sell the speed

Forty torus rings are threaded along the curve at even intervals, each oriented perpendicular to the path using curve.getTangentAt() and lookAt(), and tinted across a hue sweep. A featureless tube gives almost no sense of motion; the rings streaking past the camera are what make the flythrough read as genuine forward speed, and they double as depth markers.

Exponential fog hides the seams

FogExp2 fades the far end of the tunnel into the background color, which both hides where the finite tube geometry ends and concentrates the neon glow near the camera. Because the fog color matches the page background exactly, the WebGL canvas blends seamlessly into the surrounding scroll sections above and below the pinned stage.

scrub: 0.7 for cinematic glide

A numeric scrub value lets the camera position glide toward the scroll position over about seven-tenths of a second rather than snapping frame-perfectly. Fast camera travel through a tunnel amplifies input jitter, so this smoothing is what turns raw trackpad and wheel noise into a fluid, deliberate ride. This is the same scrubbing pattern used by the scroll camera path snippet, applied to a curved tube instead of a straight gallery. Pair it with a starfield warp intro or a synthwave terrain outro for a full retro-futurist scroll journey.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not need to reverse-engineer how a single scrubbed number can drive an entire curved flythrough. Paste this snippet's HTML, CSS, and JS into an AI assistant like Claude and ask it to walk through why the tunnel geometry and the camera share one CatmullRomCurve3, or why the look target is sampled ahead of the camera rather than at a fixed point. The same assistant can help you extend it — ask it to add a subtle roll that leans into each turn using the curve's tangent, spawn particles that stream backward past the camera for extra speed, or trigger a color pulse on the rings in time with an audio track. It can also optimize the scene, for instance merging the ring meshes into a single instanced mesh so forty draw calls collapse into one. Treat the code as a conversation starter, 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-scrubbed neon tunnel flythrough" in plain HTML, CSS, and JavaScript using Three.js, GSAP, and GSAP's ScrollTrigger plugin, all loaded from a CDN (no bundler, no build step).

Requirements:
- A pinned section containing a full-size canvas, with a WebGLRenderer and PerspectiveCamera sized to it and updated on window resize including aspect ratio.
- Build a single THREE.CatmullRomCurve3 from about a dozen control points that wander gently in X and Y while stepping steadily in negative Z.
- Extrude a THREE.TubeGeometry along that curve and render it as a wireframe (LineSegments over WireframeGeometry) so the tunnel walls glow.
- Thread roughly 40 torus rings along the same curve at even intervals, each positioned with getPointAt and oriented perpendicular to the path using getTangentAt and lookAt, tinted across a hue sweep.
- Add THREE.FogExp2 whose color matches the page background so the far end of the tube fades out and the canvas blends into surrounding scroll sections.
- Register a GSAP tween on a ScrollTrigger targeting the pinned section, with pin: true, start at top top, a numeric scrub around 0.7, and an end several hundred percent tall, animating a single plain value t from 0 to just under 1.
- Every animation frame (requestAnimationFrame, independent of the scroll callback), set the camera position to curve.getPointAt(t) and call camera.lookAt on curve.getPointAt(t + a small delta) so the camera always faces the direction of travel and banks into turns.
- Confirm scrolling back up reverses the entire flythrough, since t is fully scrubbed rather than a one-way timer.

Step by step

How to Use

  1. 1
    Load all three CDN scriptsAdd three.min.js, gsap.min.js, and ScrollTrigger.min.js from the CDN panel, in that order.
  2. 2
    Paste HTML, CSS, and JSA neon wireframe tunnel appears in a pinned 3D stage with a live depth read-out.
  3. 3
    Scroll downThe camera flies down the curved tube, passing through glowing rings tied directly to scroll position.
  4. 4
    Scroll back upThe flythrough reverses exactly, since the travel value is fully scrubbed rather than timer-based.
  5. 5
    Reshape the tunnelEdit the control-point loop to change how the curve wanders, or the TubeGeometry radius to widen it.
  6. 6
    Tune the ride lengthChange the ScrollTrigger end value (+=500%) for a longer, slower descent or a shorter, quicker one.

Real-world uses

Common Use Cases

Immersive landing intros
Open a product or event site by pulling visitors down a neon tunnel before the content resolves.
Game and esports promos
A high-speed tube ride matches the energy of arcade racers, rhythm games, and cyberpunk worlds.
Music and event pages
Sync the ring colors to a brand palette for a scroll-controlled hype reel above the lineup.
Teaching curve-based camera rigs
A compact example of driving a Three.js camera along a CatmullRomCurve3 with getPointAt and getTangentAt.
Portfolio transitions
Use the tunnel as a transition between a scroll camera path gallery and your work.
Sci-fi storytelling
Pair each ring you pass with a caption to narrate a journey, chapter by chapter, as the visitor scrolls.

Got questions?

Frequently Asked Questions

The TubeGeometry is extruded along a CatmullRomCurve3, and the camera travels that identical curve via getPointAt. Sharing one curve guarantees the camera is always centered inside the tube looking down its length, so there is never a mismatch between where the walls are and where the camera points — reshaping the curve reshapes both at once.

Facing a fixed far point makes corners feel like the wall is swinging past you. Sampling the curve a small fraction ahead of the camera's current position (t + 0.01) means the camera always aims down the direction of travel, so it banks naturally into each bend the way a vehicle follows a track.

Forty torus rings are placed along the curve at even intervals, each oriented perpendicular to the path with getTangentAt and lookAt. A bare tube gives almost no motion cue; the rings streaking past the camera are what read as real forward velocity, and they act as evenly spaced depth markers.

FogExp2 fades the distant tube into the page background color. Matching the fog to the CSS background hides where the finite geometry ends and lets the WebGL canvas blend seamlessly into the plain scroll sections above and below the pinned stage, so there is no visible edge to the 3D scene.

Yes. Click JSX for a React component, Vue for a Vue 3 SFC, Angular for a standalone component, or Tailwind for a React + Tailwind version. Build the curve, geometry, and GSAP timeline inside a mount effect against a canvas ref, and on cleanup kill the ScrollTrigger instance (or revert a gsap.context) and call renderer.dispose() so the pin and WebGL context are released on unmount.