HomeArticlesRendering

Procedural Textures: Noise, fBm & Worley Cells in GLSL

Marble, wood grain, lava and clouds, generated from nothing but maths inside a fragment shader — no image files, no texture memory.

mysimulator teamUpdated July 2026≈ 10 min read▶ Open the simulation

A texture computed, not sampled

A procedural texture is generated entirely from a mathematical function evaluated per pixel, rather than looked up from a stored image. That trade brings real advantages: infinite resolution with no blurring on zoom, seamless tiling with no visible seams, and zero texture-memory bandwidth — the whole surface detail is computed fresh every frame. Nearly every procedural texture starts from the same building block: a noise function mapping coordinates to smooth pseudo-random values. Value noise assigns a random scalar to each lattice corner and interpolates between them; it is cheap but tends to show a faint blocky "pillow" artefact because gradient information is lost across cell borders. Gradient noise — Perlin noise, and its simplex variant — assigns a random direction vector to each corner instead and interpolates the dot product between that gradient and the offset to the sample point, which removes the banding and looks convincingly organic.

perlinNoise(p):
  i = floor(p); f = fract(p)
  u = f*f*f*(f*(f*6 - 15) + 10)          // quintic fade — zero 1st & 2nd derivative
  a,b,c,d = dot(gradient(corner), offset)  for the 4 surrounding lattice corners
  return mix(mix(a,b,u.x), mix(c,d,u.x), u.y) * 0.5 + 0.5

Stacking octaves: fractional Brownian motion

A single noise call has a narrow spatial frequency — real terrain, clouds and rock have detail at many scales at once. fBm (fractional Brownian motion) sums several octaves of the same noise function, each one doubling in frequency (lacunarity) while halving in amplitude (persistence), a pattern controlled by the Hurst exponent. Raising persistence toward 0.6–0.7 injects more high-frequency energy for rough, jagged terrain; lowering it toward 0.4 produces the smoother rolling variation typical of cloud layers. Two simple transforms of the same octave sum give very different looks: taking the absolute value of each octave before summing (billow noise) produces a lumpy, cauliflower-like cloud texture, while folding it as 1 − |noise| (ridge noise) turns gentle hills into sharp mountain ridgelines.

Worley noise: cells instead of blobs

Steven Worley's 1996 cellular noise takes a completely different approach: scatter a random feature point in each cell of a grid, and for every fragment find the distance to the nearest one (F1) and second-nearest (F2). F1 alone gives filled polygonal cells that read as paving stones; F2 − F1 highlights the boundaries between cells, which is exactly the fine mesh of a reptile's scales or the cracks in dried mud. Checking only the surrounding 3×3 neighbourhood of cells is enough to guarantee the true nearest point is found, as long as feature points are jittered by less than a full cell width.

live demo · organic noise-driven motion● LIVE

From noise to material: marble, wood and warped clouds

Concrete materials come from perturbing a simple base pattern with noise. Marble is a sine-wave stripe pattern along one axis, with the input distorted by turbulence — an fBm variant that sums the absolute value of each octave — before the sine is evaluated, which bends straight stripes into veins. Wood grain takes the radial distance from a growth-axis point, perturbs it with fBm so the rings are not perfect circles, and thresholds the fractional part of that distance into alternating light and dark bands. The most powerful technique of all is domain warping (popularised by Inigo Quilez): feed the output of one fBm evaluation back in as an offset to the input coordinates of the next.

q = fbm(p)               // first warp field
r = fbm(p + q * k)        // second warp, using the first as an offset
result = fbm(p + r * k)   // final pattern — k ≈ 1–4 controls warp strength

Two or three levels of this feedback turn ordinary fBm into the swirling, organic detail seen in nebulae, flame, lava and alien terrain — at the cost of calling fBm two or three times per pixel, which is why mobile shaders often drop to 3–4 octaves per call to stay inside budget.

Frequently asked questions

Why is gradient (Perlin) noise preferred over value noise?

Value noise interpolates random scalars sitting at lattice corners, which loses gradient information at cell boundaries and produces a visible blocky or pillow-like artefact. Gradient noise instead assigns a random direction vector to each corner and interpolates the dot product with the offset to the sample point, which avoids that banding and looks organically smooth.

What does fBm actually control, and what happens if I change the parameters?

fBm sums several octaves of the same noise function at increasing frequency (lacunarity) and decreasing amplitude (persistence). Standard defaults double the frequency and halve the amplitude each octave. Raising persistence toward 0.6–0.7 keeps more high-frequency detail and produces rougher, noisier terrain; lowering it toward 0.4 produces smoother, cloud-like variation.

Is procedural texture generation expensive on the GPU?

Each octave of noise costs a handful of hash and interpolation operations, and effects like domain warping call the noise function multiple times per pixel — a double warp with 5 octaves is 15 noise evaluations per fragment. That is cheap compared with sampling a texture from memory, but on mobile GPUs it is common to cap octave counts at 3–4 or fall back to value noise to stay within budget.

Try it live

Switch between marble, wood, clouds, fire, Voronoi cells and lava in Procedural Textures, and tune scale, animation speed and palette to see how each parameter reshapes the underlying noise field, live, in your browser.

▶ Open Procedural Textures simulation

What did you find?

Add reproduction steps (optional)