Three.js Scroll Depth Parallax — GSAP Multi-Layer 3D Drift

Three.js Scroll Depth Parallax · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Depth encoded as a per-shape speed (1 - depth) so near shapes move more than far ones — true parallax
Every shape stores a home position; live position is always home plus an offset, so motion is fully reversible
Size, opacity, and spread also derive from depth, establishing near/far hierarchy in the static frame
Pointer parallax layered independently on scroll parallax, both expressed as offsets that add cleanly
Camera dolly on the same scrubbed value amplifies the sense of moving into the field
Translucent standard materials with additive-like layering across three depth bands
mix-blend-mode title sits between depth bands so shapes pass in front of and behind the type
Pinned, smoothed scrub (0.7), and fully reversible with no accumulation

About this UI Snippet

How to Build a Multi-Layer 3D Depth Parallax on Scroll With Three.js and GSAP

Screenshot of the Three.js Scroll Depth Parallax snippet rendered live

The Three.js Scroll Depth Parallax snippet scatters 34 translucent shapes across three depth bands and drifts them as the visitor scrolls, with near shapes racing upward while distant ones barely move — the classic parallax cue for depth — layering GSAP's ScrollTrigger with pointer tracking over core Three.js, all loaded from a CDN.

Depth encoded as a per-shape speed

Real parallax comes from one rule: closer things move more than farther things. Each shape is placed at a random Z between about -5 (near) and -50 (far), and from that Z a normalized depth value (0 near, 1 far) is computed once. That value becomes a speed of 1 - depth, stored on the shape. Scroll then lifts every shape by its home Y plus a drift scaled by its own speed, so the front layer surges while the back layer drifts lazily — depth you can feel without any explicit "layer" grouping.

Home position plus offset, never absolute

Every shape remembers its home position at startup, and each frame its live position is home plus a scroll-and-pointer offset. Nothing is ever set to an absolute coordinate mid-animation. This keeps the parallax perfectly reversible — at scroll position zero every shape is exactly back home — and means adding a shape is just one more entry in the field with its own remembered origin.

Size, opacity, and spread also read depth

Depth doesn't only drive motion. Nearer shapes are scaled larger, rendered more opaque, and scattered across a tighter spread, while far shapes are smaller, more transparent, and flung wider. Because all four properties derive from the same depth value, a shape reads as genuinely near or far the instant it appears — the motion parallax then reinforces a hierarchy the static frame already establishes.

Pointer parallax layered on scroll parallax

On top of the scroll drift, pointer movement nudges each shape horizontally — again scaled by its speed — and tilts the camera slightly. This produces a second, independent axis of parallax so the scene responds to the mouse even when the visitor isn't scrolling, giving the composition a living, holographic quality. The two parallax sources add cleanly because both are expressed as offsets from the home position.

A camera dolly for extra depth cueing

As scroll progresses, the camera itself dollies inward a few units, which subtly changes the perspective convergence and amplifies the sense of moving into the field rather than merely watching it shift. The dolly is driven by the same scrubbed value as the drift, so it too reverses exactly on scroll-up.

scrub: 0.7, pinned, and mix-blend text

A numeric scrub smooths both parallax sources against noisy input, and an overlaid title using mix-blend-mode: overlay sits between the depth bands so shapes appear to pass in front of and behind the type. This same offset-from-home technique underpins the parallax hero snippet in 2D; here it runs in real 3D depth. Pair it with a scroll particle assembly or a color morph centerpiece.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not have to invent the math that makes near things move faster than far things. Paste this snippet's HTML, CSS, and JS into an AI assistant like Claude and ask it to explain how depth is turned into a per-shape speed and why every shape stores a home position. The same assistant can help you extend it — ask it to add a third, mouse-independent slow drift so the field breathes on its own, tint shapes by depth for an atmospheric-perspective haze, or spawn new shapes at the far plane and recycle them as they pass the camera for an endless field. It can also optimize the scene by merging same-geometry shapes into instanced meshes to cut draw calls. Treat the code as a starting point for a conversation, 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 "3D depth parallax field" driven by scroll and pointer 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, PerspectiveCamera, and ambient + point lighting, sized and updated on window resize including aspect ratio.
- Scatter about 34 shapes (mixed geometries) across a Z range from about -5 (near) to -50 (far). For each, compute a normalized depth (0 near, 1 far) and derive: a speed of 1 - depth, a larger scale / higher opacity / tighter spread when near, and store the shape's home position and a small spin in userData.
- Register a GSAP tween on a ScrollTrigger targeting the pinned section, with pin: true, start at top top, a numeric scrub (~0.7), and an end several hundred percent tall, animating plain drift values from 0 to 1.
- Add a pointermove listener that records a normalized -1..1 mouse position.
- Every animation frame (requestAnimationFrame), set each shape's position to its home plus (a) a vertical scroll offset scaled by its speed and (b) a horizontal pointer offset scaled by its speed; spin each shape. Dolly the camera inward using the scroll value and tilt it slightly with the pointer.
- Confirm that at scroll position zero with the pointer centered, every shape is exactly at its home position, so the parallax is fully reversible with no accumulation.

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 field of translucent 3D shapes appears at varying depths behind an overlaid title.
  3. 3
    Scroll downNear shapes rise quickly while distant ones barely move, and the camera dollies gently inward.
  4. 4
    Move the pointerShapes shift horizontally by depth and the camera tilts, adding a second parallax axis.
  5. 5
    Scroll back upEvery shape returns exactly to its home position, since all motion is an offset from home.
  6. 6
    Tune density and depthChange the shape count, Z range, or drift multipliers to make the parallax subtler or more extreme.

Real-world uses

Common Use Cases

Depth-rich hero sections
Give a landing hero real 3D depth with shapes drifting past a headline as visitors scroll and hover.
Atmospheric backgrounds
Use the drifting field as a living backdrop behind copy, reacting to both scroll and pointer.
Teaching parallax math
A clear example of encoding depth as speed and layering two parallax sources as offsets from home.
Brand and agency intros
Pair the floating shapes with a scroll tunnel transition into the next section.
Editorial cover pages
Open a long-read with a title floating among 3D shapes that separate as the reader scrolls in.
Menu and title screens
A holographic, pointer-reactive field suits game menus and sci-fi interface backdrops.

Got questions?

Frequently Asked Questions

Each shape sits at a random Z, from which a normalized depth (0 near, 1 far) yields a speed of 1 - depth. Scroll lifts every shape by an amount scaled by its own speed, so near shapes move a lot and far shapes barely move. That single rule — closer moves more — is the entire basis of the parallax sense of depth.

Each frame a shape's position is its remembered home plus a scroll-and-pointer offset, never an absolute coordinate. This makes the parallax perfectly reversible (at scroll zero everything is home) and prevents drift accumulation, and it means adding a shape is just one more entry with its own remembered origin.

Yes. Nearer shapes are scaled larger, more opaque, and packed into a tighter spread; far shapes are smaller, more transparent, and spread wider. All derive from the same depth value, so a shape looks convincingly near or far the moment it renders, and the motion parallax reinforces a hierarchy the static frame already shows.

Both are expressed as offsets from each shape's home position — scroll adds a vertical offset scaled by speed, pointer adds a horizontal one — so they simply sum. Because neither writes an absolute position, they compose cleanly and both reverse to zero independently when scroll returns to the top and the pointer is centered.

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 shape field and GSAP timeline inside a mount effect against a canvas ref, attach the pointermove listener in the same effect, and on cleanup remove the listener, kill the ScrollTrigger, and call renderer.dispose() so listeners and the WebGL context are released on unmount.