Three.js Scroll Shader Fluid Gradient Wave — GLSL ShaderMaterial Driven by Scroll

Three.js Scroll Shader Fluid Gradient Wave · Scroll · Plain HTML, CSS & JS · Live preview

What's included

Features

Full-viewport plane rendered with an OrthographicCamera for pixel-perfect coverage at any aspect ratio
Real, compiling GLSL ShaderMaterial — a passthrough vertex shader and a wave/gradient fragment shader
Three layered sine/cosine wave terms combined into one organically fluid band value
Three-color gradient mixed via chained smoothstep()/mix() calls, no image texture required
A single uProgress uniform drives wave speed, amplitude, and color-mix hue shift together
Independent uTime uniform keeps the surface animating continuously, decoupled from scroll
Thin computed sheen highlight along the wave crest for a glossy, liquid-like polish
Fully reversible — scrolling up calms the waves and reverts the palette to its resting state

About this UI Snippet

How to Build a Scroll-Driven Fluid Gradient Wave Shader With Three.js

Screenshot of the Three.js Scroll Shader Fluid Gradient Wave snippet rendered live

The Three.js Scroll Shader Fluid Gradient Wave snippet renders a single full-viewport plane with a custom THREE.ShaderMaterial whose fragment shader combines several sine and cosine waves into a smoothly undulating color band, then mixes three colors across that band — and as the visitor scrolls through a pinned section, both the wave's amplitude/speed and the gradient's color mix shift together, driven entirely by a uProgress uniform.

Layered waves, not one sine function

The shader sums three separate wave terms — wave1 (a fast horizontal sine), wave2 (a diagonal sine combining both UV axes), and wave3 (a slower vertical cosine) — each with its own frequency and phase speed, into a single band value derived from uv.y plus all three waves. Layering multiple simple waves like this is what keeps the result reading as organically fluid rather than as one obviously periodic ripple — the same layering principle used in Scroll Canvas Generative Flow Field's procedural vector field, applied here inside a fragment shader instead of a Canvas2D loop.

A three-color gradient mixed by band position

Two chained mix()/smoothstep() pairs blend from a deep base color through a mid tone into a bright accent color, based on where band falls — since band already has the wave terms baked in, the color transitions themselves visibly ripple and shift, rather than sitting on top of a static gradient.

One uniform driving three things at once

uProgress simultaneously scales speed (how fast the waves animate), amp (how pronounced the waves are), and hueShift (how far the mid and accent colors drift toward a secondary palette via their own mix() calls) — so scrolling further doesn't just speed up the waves, it also intensifies them and shifts the whole gradient's mood, all from one float written into the material each frame.

A thin sheen highlight for extra polish

A narrow smoothstep band around band == 0.5 adds a subtle bright sheen line that rides along with the wave crest, a cheap way to suggest a glossy, liquid-like surface without any lighting model or normal calculation.

uTime versus uProgress

Exactly as in Three.js Scroll Shader Ripple Distortion, uTime advances continuously from a THREE.Clock so the surface never fully stops moving, while uProgress is scroll-bound and controls only the wave's intensity and speed multiplier — two uniforms with two distinct jobs.

Customizing it

Swap the three base colors for your own palette, add a fourth wave term at yet another frequency for more visual complexity, or change hueShift's target colors for a completely different mood shift as the user scrolls. Pair it with Three.js Scroll Shader Ripple Distortion for a related uniform-driven GLSL technique using radial rather than banded waves.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not need to derive multi-wave shader composition or uniform-driven color shifting from scratch. Paste this snippet's HTML, CSS, and JS into an AI assistant like Claude and ask it to explain exactly why layering three differently-tuned sine/cosine terms into one band value produces an organically fluid look instead of a single mechanical ripple, and how the same uProgress uniform manages to control wave amplitude, wave speed, and gradient color mix simultaneously. The same assistant can help you extend it — ask it to add a fourth wave term for even more complexity, expose the base palette as easily swappable uniforms, or add a subtle grain/noise overlay for a more textured, less flat-shaded look. 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-driven fluid gradient wave" in plain HTML, CSS, and JavaScript using Three.js with a custom ShaderMaterial, plus 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, rendered with an OrthographicCamera set to view exactly [-1, 1] on both axes, and a single PlaneGeometry(2, 2) mesh using a custom THREE.ShaderMaterial, so the plane fills the viewport at any aspect ratio.
- A vertex shader that is a standard passthrough: pass the built-in uv attribute to a varying vUv, and set gl_Position using projectionMatrix * modelViewMatrix * vec4(position, 1.0).
- A fragment shader that sums at least three separate wave terms (a mix of sin() and cos(), varying frequency, axis combination, and phase speed) into one combined "band" value derived from the UV coordinate, producing a result that looks organically fluid rather than a single obvious periodic ripple.
- Two uniforms driving the animation: a uProgress float uniform (updated every frame from a value kept in sync with scroll position) that scales the waves' amplitude and speed together, and a uTime float uniform (updated every frame from an elapsed-time clock, independent of scroll) that drives the waves' continuous phase so the surface never fully stops moving.
- Render a smooth gradient of at least three colors mixed based on the combined band value using chained smoothstep()/mix() calls, and blend the gradient's mid and accent colors toward a secondary palette as uProgress increases, so scrolling further shifts both the wave's motion and the gradient's overall color mood.
- Add a thin, subtly brighter sheen highlight computed with smoothstep around one specific band value, to suggest a glossy liquid surface with no actual lighting model.
- Register a GSAP tween on a ScrollTrigger targeting the pinned section, with pin: true, start at top top, a numeric scrub, and a multi-hundred-percent end, animating a single plain progress value from 0 to 1 with linear easing, and write that value into the uProgress uniform every animation frame.
- Confirm scrolling back up smoothly calms the waves and reverts the gradient toward its resting color mix.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<section class="fgw-stage" id="fgwStage">
  <div class="fgw-intro-overlay"><p>Scroll ↓ to stir the gradient</p></div>
  <canvas id="fgwCanvas"></canvas>
  <div class="fgw-hud"><span id="fgwPct">0</span>% amplitude</div>
</section>
<section class="fgw-bottom"><p>A single ShaderMaterial plane, waving entirely on the GPU.</p></section>

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 JSAn undulating full-viewport gradient plane renders inside a pinned stage with a live "% amplitude" read-out.
  3. 3
    Scroll downThe waves grow faster and more pronounced, and the gradient shifts toward a secondary color palette.
  4. 4
    Scroll back upThe waves calm and the color mix reverts to its resting palette exactly in reverse.
  5. 5
    Retune the wavesAdjust each wave term’s frequency and phase-speed multiplier in the fragment shader for a different rhythm.
  6. 6
    Swap the paletteChange colorA/colorB/colorC (and their hueShift targets) for a completely different mood.

Real-world uses

Common Use Cases

Generative art and shader portfolios
Showcase real GLSL work with an effect that visibly demonstrates uniform-driven wave and color animation.
Agency, studio, or SaaS hero sections
A calm, premium animated gradient backdrop that intensifies as the visitor engages by scrolling.
Music and wellness/meditation app launches
A flowing, liquid gradient suits ambient, calming, or audio-visual brand contexts.
Teaching multi-wave shader composition
A compact, real example of layering several sine/cosine terms into one organic-feeling band value.
Interactive experience or game landing pages
Pair with Three.js Scroll Shader Ripple Distortion for a two-part shader showcase.
Related: Scroll Canvas Generative Flow Field
See Scroll Canvas Generative Flow Field for a related layered-wave technique built in Canvas2D instead of GLSL.

Got questions?

Frequently Asked Questions

A single sine wave reads as an obviously mechanical, uniform ripple. Summing three terms at different frequencies, axis combinations, and phase speeds (wave1, wave2, wave3) into one band value produces a result that looks organically fluid, since no single periodic pattern dominates the visible motion.

uProgress is a single float updated every animation frame from a value kept in sync with a scrubbed GSAP ScrollTrigger tween. It scales the waves’ speed and amplitude, and also drives a hueShift value used to blend the gradient’s mid and accent colors toward a secondary palette — so scrolling further makes the surface simultaneously wavier, faster, and more vividly colored.

uTime advances continuously from a THREE.Clock every frame regardless of scroll position, keeping the wave phase animating even if the user stops scrolling. uProgress only reflects scroll position and controls intensity/speed multipliers, not the base animation phase — the two uniforms serve distinct roles.

A narrow smoothstep window centered on band == 0.5 (using two chained smoothstep calls to build a thin band rather than a hard edge) adds a small amount of extra brightness only where the band value passes through that midpoint, producing a bright line that rides along the wave crest with no lighting model or normal-vector calculation needed.

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 renderer, shader material, and GSAP ScrollTrigger inside a mount effect keyed to a canvas ref, and on unmount kill the ScrollTrigger instance, dispose the geometry and material, and call renderer.dispose().