DevTools Logo
All posts

One Pixel at a Time: Thinking in Fragment Shaders

August 27, 2026 · DevTools

glsl
webgl
shaders
raymarching
creative coding

The first shader everyone writes is a gradient: forty lines of setup for something a CSS one-liner already does. The second shader is where the epiphany lives — the moment you realize a fragment program isn't drawing a picture, it's answering the same question two million times per frame: what color is this pixel?

Coordinates are your entire world

gl_FragCoord.xy divided by iResolution gives you uv — the normalized screen position, zero to one in each axis. That's all a fragment shader knows. No scene graph, no object list, no memory of the previous frame (unless you build one). Every illusion of structure — a sphere, a grid, rain — has to be computed from those two numbers and time. Once that clicks, the classic patterns become obvious: plasma is sines of uv, a vignette is length(uv - 0.5), tiling is fract(uv * n).

Field functions beat geometry

The trick that unlocks everything else: instead of asking "what objects are here," ask "how far am I from the nearest surface." A signed distance function (SDF) answers that for any point in space — length(p) - r for a sphere, a rotated circular cross-section for a torus. March a ray forward by the reported distance, repeat until you're inside a surface or too far, and you've raymarched: full 3D rendering from a hundred lines of math, no meshes, no matrices. The Mandelbulb works the same way, except the distance estimator comes from triplex fractal iteration — which is why a shape no polygon pipeline could ever tessellate renders in real time on a laptop GPU.

Noise is structure you can tune

Pure sines look mechanical; pure random looks like static. Value noise and its fractal octave-stacked cousin FBM live in between, and nearly every natural texture is some blend: clouds are FBM warped by more FBM, marble is sin(x + fbm(p) * strength), Voronoi cells (hash-labeled nearest-neighbor points) give crystals, stones and the shatter pattern in every sci-fi UI. Learn to see those three families in screenshots and you can usually reverse-engineer a shader in minutes.

Performance is visible, not theoretical

CPU code hides its cost behind profiles and flame graphs; a fragment shader prints its cost in FPS, every frame, as you type. The physics: your function runs once per pixel, so cost scales with window area — fullscreen 1080p is two million invocations, and every extra loop iteration multiplies by that number. The disciplines follow naturally: cap raymarch steps, hoist invariants out of loops, avoid texture fetches you can compute, and prefer smoothstep over branches. When an edit halves your FPS, you learned something no blog post could teach.

From tab to production

The quiet virtue of a browser shader lab is the export. The math you just tuned doesn't need to be re-typed into a different toolchain: the same GLSL drops into a Three.js ShaderMaterial, an R3F component with proper uniform wiring, or a standalone HTML page with zero dependencies. A landing-page background stops being "design idea, blocked on engineering" and becomes an afternoon: prototype live, watch the FPS, export, ship.

Start with the plasma. Then change one number and watch the whole field respond. That feedback loop — math in, light out, sixty times a second — is the entire addiction of shader programming.