What is a Fragment Shader?
Let us begin with the most honest question possible: What is a fragment shader?
In traditional programming, code executes sequentially. A Central Processing Unit (CPU) reads instructions one by one. If you instruct a CPU to paint a screen consisting of two million pixels, it paints pixel one, then pixel two, up until pixel two million. It is incredibly intelligent, but restricted by linearity.
A Graphics Processing Unit (GPU) possesses thousands of small, specialized cores. A Fragment Shader is a function written in GLSL (OpenGL Shading Language) that is sent to the GPU. The GPU executes this exact same function for every single pixel on the screen simultaneously.
Because the code runs perfectly in parallel, it is extraordinarily fast. However, it imposes a severe constraint: Pixel A has no knowledge of what Pixel B is doing. You cannot use traditional loops to draw lines from one point to another. You must express your entire visual intent as pure mathematical geometry based exclusively on the pixel's coordinate on the screen.
01. The Gradient (UV Space)
We begin by normalizing the screen coordinates. gl_FragCoord.xy provides the exact pixel location (e.g., 800x600). Dividing this by u_resolution.xy yields our uv vector, ranging from 0.0 to 1.0 on both axes. Mapping uv.x to the Red channel and uv.y to the Green channel generates an absolute gradient.
02. The Perfect Circle
In GLSL, we draw shapes using math, not commands. By measuring the distance length() from the current pixel to the center of the screen vec2(0.5), we acquire a radial gradient. The smoothstep() function acts as a threshold, converting that gradient into a solid, anti-aliased circle.
03. The Grid
The fract() function is a cornerstone of generative logic. It returns only the fractional part of a number (e.g., fract(1.2) = 0.2). By multiplying our uv coordinates by 10 and applying fract(), we instantly subdivide our space into a repeating 10x10 grid. The step() function draws the sharp boundary lines.
04. Procedural Noise
GLSL does not possess a native random() function. To generate noise, we manipulate sine waves destructively. We take the dot product of our coordinates against an arbitrary large vector, apply a sine function, and multiply it by a massive number (43758.5453) to obliterate any visible pattern.
05. Cosine Color Cycling
Introduced by Inigo Quilez, this algorithm utilizes a simple cosine wave offset by different phases for Red, Green, and Blue channels. By injecting u_time (a uniform variable constantly updated by JavaScript) into the equation, the colors cycle infinitely and organically.
06. Domain Warping
Instead of animating the color, we can animate the fabric of space itself. By adding a time-driven sine wave to the uv.x coordinate based on the uv.y coordinate, the space distorts, generating fluid, flag-like movements.
07. Fractal Brownian Motion (fBM)
Pure static noise is harsh. Nature exhibits structure. FBM superimposes multiple layers (octaves) of smoothed noise, progressively increasing the frequency and decreasing the amplitude. This algorithm is the architectural foundation of rendering digital clouds, mountains, and fog.
08. Domain Repetition
Returning to the fract() function: if we multiply our coordinates, apply fract(), and subtract 0.5, we reset the center of our coordinate system multiple times across the screen. Drawing a single circle within this warped domain yields an infinite array of identical circles.
09. Simple 3D Raymarching
Shaders operate in 2D, but we can simulate 3D via Raymarching. We cast a virtual ray from a camera (Ray Origin) through every pixel on the screen (Ray Direction), stepping forward until we intersect a mathematical Signed Distance Field (SDF) of a sphere.
10. Compositing & Mixing
Real generative art relies on layering. By calculating multiple masks (e.g., two moving circles) and utilizing the mix() function, we can interpolate between diverse colors seamlessly based on intersecting geometries.