Skip to content
← ALL EXPERIMENTS

09 / GLSL / 2026-04-11

Gradient Forge

Three-stop GLSL gradient with animated noise.

How it works

// 01 DPI and pixel density

A full-screen quad (two triangles covering the canvas) is drawn once. The fragment shader runs for each pixel and calculates its color. The pixel's normalized x position determines where it falls on the gradient axis. Below the first stop position, it reads the first color. Between stops, it interpolates linearly. The noise function perturbs the axis position before sampling, which softens the transitions into organic blends.

// 02 Bleed and cut tolerance

The noise is classic 2D simplex noise seeded with time, so every frame the field shifts slightly and the gradient feels alive without looking animated in a cartoon way. Amplitude controls how far the noise can push a pixel's sample position along the gradient axis. A small amplitude (0.02) reads like a clean gradient with a hint of drift. A large amplitude (0.3) looks like a color cloud.

// 03 Safe zone and trim accuracy

The shader compiles once on mount and stays resident. Uniforms (color stops, positions, amplitude, time) are updated each frame via `gl.uniform3f` and `gl.uniform1f` calls, which are cheap. Resizing the canvas only updates the viewport dimensions, not the shader itself. Total GPU work per frame is bounded by the pixel count of the canvas, which is why the effect runs at full frame rate even on mid-tier mobile hardware.

OBJECT / gradient.fragGLSL
......
......

// why this exists

shader literacy

CSS gradients are fine for a button. They fall apart the moment you want a living background, a hero that breathes, or a color field that refuses to band. This widget is a small GLSL playground that renders a three-stop gradient with animated Perlin-style noise directly on the GPU. Pick three colors, set the stop positions, dial in the noise amplitude, and watch the surface move.

GLSL stands for OpenGL Shading Language. It is the language a browser speaks when it needs to hand drawing work to the graphics card instead of the CPU. Every pixel on the canvas runs the same tiny program (a fragment shader) in parallel, which is why a shader can render a smooth 1920 by 1080 gradient at 60 frames per second on a phone that would choke on the same effect in canvas2D.

The three stop positions define where each color sits along the gradient axis. The shader blends linearly between them, then perturbs the position with a noise field so the boundaries between colors stop being rigid bands and start looking like atmosphere. The noise is tuned to preserve the overall gradient direction while adding a slow, organic drift. Turn the amplitude up and the gradient becomes a cloud. Turn it down and it looks like a clean CSS gradient that happens to animate.

I built this because I kept reaching for the same effect in client work and wanted a canvas I could tune in seconds instead of opening a shader IDE. The code is small enough to copy verbatim into a Next.js component. The performance budget is small enough that you can ship it as a hero background without worrying about the lighthouse score. The output looks nothing like a typical brand hero, which is the entire point.

Export is not implemented in this version. Copy the shader source from the inspector or grab a screenshot for comp work. The color stops persist in the URL hash, so a tuned gradient is a link away from a teammate.

Frequently asked questions

What is a fragment shader?

A tiny program that runs on the graphics card once per pixel, in parallel. It takes coordinates and uniforms as input and returns a color. Fragment shaders are how modern browsers render effects that would melt a CPU.

Why GLSL instead of canvas2D for a gradient?

Canvas2D renders pixel by pixel on the CPU. A 1920 by 1080 gradient with animated noise in canvas2D runs at 5 to 10 fps. The same effect in GLSL runs at 60 fps on a phone.

What is simplex noise?

A gradient noise function invented by Ken Perlin's successor that produces smoother visual results than classic Perlin noise and scales better to higher dimensions. It is the standard noise function for shader art.

Can I export the gradient as an image?

Not from the widget directly. Take a screenshot or grab the canvas with devtools. A PNG export is a reasonable next addition.

Will this work on older browsers?

Anything that supports WebGL 1, which is every browser shipped since 2013. If WebGL is disabled, the widget shows a static CSS fallback.

How do I reuse the shader in my own site?

Copy the fragment shader source and the small WebGL setup wrapper. The shader has no dependencies and the wrapper is about 40 lines.

Does this drain my phone's battery?

Less than you'd think. The shader only runs while visible (IntersectionObserver gated) and the per-frame GPU work is small. Flagship phones report under 2% battery per hour of continuous render.

Can I add a fourth color stop?

Yes, with a small shader edit. The blend logic is a linear chain, so add another mix call and expose another uniform.

Why does my custom gradient look banded?

Too low amplitude and too similar stop colors. Increase noise amplitude or widen the color contrast between stops. Also check that the canvas is rendering at device pixel ratio.