Skip to content

Lab.

GitHub →

The Idea

Scrolling is almost always linear. A lab question: what if the scroll position didn't just move content but disturbed it — the way moving your hand through water warps whatever is beneath the surface? The goal was to turn scroll velocity into a fluid post-processing effect, so the entire scene behaves like a pond being rippled by the reader's own gesture.

The Shader

The heart of the effect is a custom WaterPass post-processing pass. Inside the fragment shader, each pixel's UV is offset by a pair of sine/cosine waves driven by time and a dynamic factor:

GLSL — fragment shader
uv.x += 0.5 * amplitude * cos(uv.y * 4.0 + time * 0.7);
uv.y += 0.5 * amplitude * sin(uv.x * 4.0 + time * 0.3);

When factor is zero the scene renders cleanly. As it grows, the screen distorts into rolling water — everything the camera sees, text and images included, inherits the wave.

The Drive

The shader is useless without a good input signal. Raw scroll delta is jittery, so I keep a rolling buffer of the last 10 frame-to-frame scroll deltas and feed the running average into factor, lerped at 0.1 per frame. The result: quick flicks produce an elastic surge, slow drags produce a gentle swell, and stopping the scroll lets the water calm back to glass.

The Layout

Typographic pages and image grids are arranged with @react-three/flex, which brings CSS-like flex semantics into the Three.js scene graph. That made it possible to compose a scrollable "magazine" entirely inside the canvas — the same surface that the water shader later distorts — instead of juggling two separate DOM and WebGL layouts.

Stack

  • React
  • Three.js
  • React Three Fiber
  • @react-three/drei
  • @react-three/flex
  • @react-spring/web
  • GLSL
  • Create React App