Tunnel
A closed CatmullRomCurve3 loop — radius and height both wobbling at different frequencies — extruded into a TubeGeometry rendered BackSide so the camera flies through its interior. There's no orbiting or dragging here: the camera's position and look-at target are both computed from the curve every frame, so it just flies the loop forever. Five point lights seated directly on the path wash the walls in alternating violet/cyan/magenta as the camera passes them, and a second wireframe-only copy of the same tube geometry gives the corridor a scrolling grid so speed and curvature stay legible. Scroll over the canvas to speed up or slow down the flight.
How it works
- The path:
flightPath = new CatmullRomCurve3(pathPoints, true, ...)— thetruecloses the loop.pathPointsisn't a circle; radius and height each vary by a different sine/cosine frequency so the tube reads as an organic warped corridor. - Camera path animation: the tube mesh's
onFrame: (root, delta) => {...}advancesflightPosition(a loop-fraction,0..1) bydelta * speedeach frame, placesroot.camera.positionatflightPath.getPointAt(flightPosition), and callsroot.camera.lookAt(...)at a point a little further ahead on the curve (LOOK_AHEAD) — that's what keeps the camera always facing its direction of travel through the bends. See Animation & Loop. onWheel: attached to the tube mesh itself, since the camera is always inside it — the tube is the one surface guaranteed to be under the pointer. Per the function-prop rules,onWheelis a raycast pointer event, so the handler receives aThreeEvent<WheelEvent>withdeltaYcopied straight onto it; the handler clampsspeedinto[SPEED_MIN, SPEED_MAX]and callsevent.nativeEvent.preventDefault()so the wheel doesn't also scroll the docs page underneath the canvas. See Events.- Two tubes, one geometry shape: the visible corridor is a
meshStandardMaterial(side: BackSide) tube for the lit surface, and a siblingmeshnode builds a secondTubeGeometryfrom the sameargsvalues with awireframe: true, transparent: truemeshBasicMateriallayered on top — a cheap way to get a scrolling grid overlay for motion legibility without hand-building line geometry. - Lights on the path:
tunnelLightsmaps 5 colors toflightPath.getPointAt(index / 5)positions and spreads them intoscene— plainpointLightnodes at physical intensities (70,distance: 10,decay: 2), so each one only lights the stretch of tube nearby before falling off. - Fog matches the backdrop:
{ fog: null, args: [BACKDROP_COLOR, 8, 30] }(inferredattachvia.isFog) uses the same color asscene.background, so the tunnel dissolves into the void a short way ahead instead of cutting off hard. See Attach inference. - Initial camera framing:
camera: { position: [...], fov: 80, near: 0.05, far: 60 }starts the camera atflightPath.getPointAt(0)with a wide FOV suited to flying through a tight corridor, andonCreatedsets the initiallookAtso the first rendered frame already faces down the path, before the firstonFrametick runs.