02ResearchJul 25, 2026 · 1 min read
Flowing noise fields with a single fragment shader
A practical walkthrough of building the animated green background on this site: simplex noise, fbm, and a fullscreen plane in react-three-fiber.
#webgl#shaders#three.js
The hero background is one mesh — a fullscreen plane with a custom fragment shader. No geometry, no lights, just math per pixel.
The recipe
- Render a plane that covers clip space (
position.xystraight togl_Position). - Sample fractal Brownian motion (layered simplex noise) in the fragment shader.
- Map the noise value onto a three-stop green gradient.
- Drift the sample coordinates over
uTimeand nudge them with the pointer.
fbm in a nutshell
float fbm(vec2 p){
float value = 0.0;
float amp = 0.5;
for (int i = 0; i < 5; i++){
value += amp * snoise(p);
p *= 2.0; // higher frequency
amp *= 0.5; // lower amplitude
}
return value;
}
Each octave adds finer detail. Five octaves is plenty for a soft, cloudy field.
Findings
- DPR matters. Clamp
dprto[1, 1.75]; beyond that the GPU cost jumps with little visual gain. - Lerp the pointer. Feeding raw pointer values makes the motion jittery;
smoothing with a small lerp factor (
0.04) feels alive but calm. - Respect reduced motion. Swap the canvas for a static gradient when the user asks for less movement.
The whole effect is a few dozen lines of GLSL. The restraint is in the palette, not the complexity.