Wave Tunnel
Wave Tunnel is a particle background in the UI HUB particle backgrounds collection. A scroll-dive through a point-cloud wave tunnel rendered with Three.js ShaderMaterial + BufferGeometry.
The component is driven by 5 documented props (className, style, progress, compact, showDemoButton), so colours, intensity, timing and density can be tuned from the call site instead of being hard-coded.
Implementation notes: Three.js ShaderMaterial with custom GLSL vertex/fragment; WebGL BufferGeometry with aIJ index attribute; PerspectiveCamera with per-frame projectionMatrix lens shift.
Preview the particle background in the browser above, copy the source, and paste it into your project. Particle Backgrounds components in UI HUB are grouped by category so you can compare effects side by side before choosing one.
What is Wave Tunnel?
A scroll-dive through a wave-tunnelled point cloud: 17,100 white Three.js points in rings that curl one revolution per tunnel length, rippled by sinusoids + noise in the vertex shader, with the camera inside the barrel, a lens-shifted vanishing point, pointer push-back, and a parallax section HUD over a 600vh dive.
Wave Tunnel at a glance
- Category
- Particle Backgrounds
- Pricing
- Free to use
- Libraries
- React, Three.js and Framer Motion
- Language
- TypeScript
- Styling
- Tailwind CSS
- Configurable props
- 5
- AI prompts
- Ready for Advance, Antigravity, Claude Code, Cursor, Lovable
How Wave Tunnel works
A scroll-dive through a point-cloud wave tunnel rendered with Three.js ShaderMaterial + BufferGeometry. nu=90 rings x nv=190 points are stored as aIJ index pairs (no positions); in the vertex shader u = fract((aIJ.x + uPhase + uTravel)/nu) flows by exactly one cell per 2s loop, v = aIJ.y/(nv-1), theta = v * turns(1) * 2PI. Radius is rOuter(3.4) * uShrink * (1 - 0.10*v) tapered mix(1, 0.55, smoothstep(0.35,1,u)), rippled by three traveling terms (0.10*sin(th*3 + u*18 - w) + 0.06*sin(th*7 - u*30 + 2w) + 0.18*(fractalNoise-0.5), w = uTime*2PI/loopSeconds) with intensity eased off by ease = pow(u,1.6); depth z = 1.2 + ease*length(12). p.xy -= hole[1.5,0.9] so the camera sits off-axis inside the barrel. A vanishing-point lens shift (applyLens: projectionMatrix elements[8] = -(vp[0]*(1-progress*0.9)), [9] = -(vp[1]*(1-progress))) pitches the tunnel, fov 70 (85 when portrait). Scrolling dives uTravel = progress * nu * 1.6, shrinks the barrel to 70%, rolls rotation.z by progress * 1.4 (progress smoothed 0.07 toward the raw scroll target); the pointer eases camera tilt/look through tilt 0.12 and pushes points away in screen space (dir * exp(-|d|^2/mouseRadius^2) * mouseStrength, d.x scaled by aspect) with uMouseOn lerping in on hover. Points render white (vec4(vec3(1.), vA*smoothstep(.5,.28,dist)), vA = smoothstep(1,.82,u)*smoothstep(0,.03,u)), transparent depthTest off, point size clamp(uPx*5/depth, 1, uPx*1.15) with uPx = pointPx(3)*dpr, frustumCulled false, preserveDrawingBuffer for deterministic captures. A parallax section HUD overlays four sections (01 Into the wave / 02 Depth / 03 Motion / 04 Beyond): huge outline numeral on the fast layer, title on the mid layer, tagline on the slow layer, with opacity/scale/blur keyed to scroll, plus a top progress hairline (scaleX), a bottom scroll hint fading out by 6% scroll, and a 36px spring cursor ring in mix-blend-difference with cursor:none. Layout is a 600vh scroll sequence with a sticky full-viewport stage and slow canvas parallax (y 0% -> -4%, scale 1.04 -> 1.18). Reduced motion freezes the wave clock (t = 0) while keeping the scroll dive; a ?t= query param captures a deterministic frame. compact mode swaps window scrolling for an internal overflow shell (overscroll-behavior contain); resize observer re-sizes renderer/camera; geometry, material, renderer, listeners, observer and the rAF loop are disposed on unmount.
Technical information for Wave Tunnel
| Category | Particle Backgrounds |
|---|---|
| Libraries | React, Three.js and Framer Motion |
| Styling | Tailwind CSS |
| Language | TypeScript |
| Rendering | HTML canvas, spring physics, blend modes |
| State change | barrel at rest with the wave rippling and hint visible at the top of the dive to deep dive with the pointer pushing points back, section titles cascading past, the top hairline filling and the camera rolling |
| Props | 5 configurable |
| Usage example | Included on this page |
What Wave Tunnel requires
- Three.js ShaderMaterial with custom GLSL vertex/fragment
- WebGL BufferGeometry with aIJ index attribute
- PerspectiveCamera with per-frame projectionMatrix lens shift
- requestAnimationFrame loop (THREE.Clock)
- ResizeObserver on the gl container
- Scroll listener on the internal shell (compact) or window (full)
- Pointer listeners on window feeding a lerped target + uMouseOn
- prefers-reduced-motion matchMedia (freezes wave clock, keeps scroll)
- framer-motion useMotionValue/useSpring/useTransform for parallax HUD
- React Router Link for the demo pill
How to use Wave Tunnel
- Install the dependencies: npm install three and npm install framer-motion.
- Copy the usage example from the Source section of this page. It shows the import path and the props the component expects.
- Render <WaveTunnel /> in your page using that import path.
- Pass any of the 5 documented props to change colours, density, speed or behaviour.
Wave Tunnel questions
What is Wave Tunnel?
A scroll-dive through a wave-tunnelled point cloud: 17,100 white Three.js points in rings that curl one revolution per tunnel length, rippled by sinusoids + noise in the vertex shader, with the camera inside the barrel, a lens-shifted vanishing point, pointer push-back, and a parallax section HUD over a 600vh dive.
How does Wave Tunnel work?
A scroll-dive through a point-cloud wave tunnel rendered with Three.js ShaderMaterial + BufferGeometry. nu=90 rings x nv=190 points are stored as aIJ index pairs (no positions); in the vertex shader u = fract((aIJ.x + uPhase + uTravel)/nu) flows by exactly one cell per 2s loop, v = aIJ.y/(nv-1), theta = v * turns(1) * 2PI. Radius is rOuter(3.4) * uShrink * (1 - 0.10*v) tapered mix(1, 0.55, smoothstep(0.35,1,u)), rippled by three traveling terms (0.10*sin(th*3 + u*18 - w) + 0.06*sin(th*7 - u*30 + 2w) + 0.18*(fractalNoise-0.5), w = uTime*2PI/loopSeconds) with intensity eased off by ease = pow(u,1.6); depth z = 1.2 + ease*length(12). p.xy -= hole[1.5,0.9] so the camera sits off-axis inside the barrel. A vanishing-point lens shift (applyLens: projectionMatrix elements[8] = -(vp[0]*(1-progress*0.9)), [9] = -(vp[1]*(1-progress))) pitches the tunnel, fov 70 (85 when portrait). Scrolling dives uTravel = progress * nu * 1.6, shrinks the barrel to 70%, rolls rotation.z by progress * 1.4 (progress smoothed 0.07 toward the raw scroll target); the pointer eases camera tilt/look through tilt 0.12 and pushes points away in screen space (dir * exp(-|d|^2/mouseRadius^2) * mouseStrength, d.x scaled by aspect) with uMouseOn lerping in on hover. Points render white (vec4(vec3(1.), vA*smoothstep(.5,.28,dist)), vA = smoothstep(1,.82,u)*smoothstep(0,.03,u)), transparent depthTest off, point size clamp(uPx*5/depth, 1, uPx*1.15) with uPx = pointPx(3)*dpr, frustumCulled false, preserveDrawingBuffer for deterministic captures. A parallax section HUD overlays four sections (01 Into the wave / 02 Depth / 03 Motion / 04 Beyond): huge outline numeral on the fast layer, title on the mid layer, tagline on the slow layer, with opacity/scale/blur keyed to scroll, plus a top progress hairline (scaleX), a bottom scroll hint fading out by 6% scroll, and a 36px spring cursor ring in mix-blend-difference with cursor:none. Layout is a 600vh scroll sequence with a sticky full-viewport stage and slow canvas parallax (y 0% -> -4%, scale 1.04 -> 1.18). Reduced motion freezes the wave clock (t = 0) while keeping the scroll dive; a ?t= query param captures a deterministic frame. compact mode swaps window scrolling for an internal overflow shell (overscroll-behavior contain); resize observer re-sizes renderer/camera; geometry, material, renderer, listeners, observer and the rAF loop are disposed on unmount.
Which category does Wave Tunnel belong to?
Wave Tunnel is listed under Particle Backgrounds in the UI HUB catalog. Particle backgrounds render tens of thousands of points on the GPU: spheres, halos, fields and constellations that drift, glow and react to the pointer. They are the heaviest components in the library, so each one lists its rendering approach and requirements before you copy it. Use them as hero backdrops, section backgrounds or full-page intros where a static gradient would fall flat.
What does the className prop do in Wave Tunnel?
Optional class applied to the root container. It defaults to "".
What does the style prop do in Wave Tunnel?
Optional inline styles for the root container. It defaults to {}.
Is Wave Tunnel free to use?
Wave Tunnel is a free component on UI HUB. You can preview it in the browser and read the full props table on the component page.
Which libraries does Wave Tunnel use?
Wave Tunnel uses React, Three.js and Framer Motion and is styled with Tailwind CSS.
What does Wave Tunnel need to run?
Wave Tunnel needs Three.js ShaderMaterial with custom GLSL vertex/fragment, WebGL BufferGeometry with aIJ index attribute, PerspectiveCamera with per-frame projectionMatrix lens shift and requestAnimationFrame loop (THREE.Clock).