HomeArticlesProcedural Terrain

Procedural Terrain: fBm Noise to Infinite Landscapes

How layered gradient noise, hillshading and simulated water erosion turn a random seed into a believable landscape.

mysimulator teamUpdated June 2026≈ 7 min read▶ Open the simulation

Noise that looks like nothing, until you layer it

Generating a believable landscape starts from gradient noise (Perlin-style or simplex noise): a smooth, pseudo-random function of position with no sharp jumps, generated deterministically from a fixed seed so the same (x, y) always returns the same height. On its own a single noise layer looks like smooth, rolling, featureless hills — nothing like a real mountain range, which has detail at many different scales simultaneously.

live demo · fBm heightmap with hillshading● LIVE

Fractal Brownian Motion: stacking octaves

Fractal Brownian Motion (fBm) fixes this by summing several octaves of the same noise function at increasing frequency and decreasing amplitude — each octave adds finer detail, like adding boulders on top of hills on top of a mountain range:

function fbm(x, y, octaves, lacunarity, gain) {
  let total = 0, amplitude = 1, frequency = 1, maxValue = 0;
  for (let i = 0; i < octaves; i++) {
    total     += noise(x * frequency, y * frequency) * amplitude;
    maxValue  += amplitude;
    amplitude *= gain;         // typically 0.5 - each octave weaker
    frequency *= lacunarity;   // typically 2.0 - each octave finer
  }
  return total / maxValue;     // normalised to roughly [-1, 1]
}

Lacunarity controls how much finer each successive octave is (2.0 doubles the frequency each time, the classic choice); gain (sometimes called persistence) controls how quickly amplitude falls off, and therefore how "rough" the terrain looks — a gain near 0.5 gives natural-looking mountains, while a higher gain keeps high-frequency detail loud and produces jagged, alien terrain. This layered-noise construction approximates 1/f noise (pink noise), the same statistical texture measured in real coastlines and mountain silhouettes, which is why fBm terrain reads as natural rather than random-looking.

From height to biome: hillshading and colour

A heightmap alone is a grey blob; it becomes a readable landscape once shaded and coloured. Hillshading computes the local surface normal from the height differences between neighbouring cells and dot-products it with a fixed sun direction, brightening slopes that face the light and darkening those that face away — the same technique cartographers have used by hand since the 19th century. Biome colouring then thresholds height (and sometimes a second, independent noise layer for moisture) into bands: deep blue below sea level, sand near the coast, green at moderate elevation, grey rock and white snow at the peaks.

Hydraulic erosion: making the noise look weathered

Raw fBm never erodes, so its ridgelines look uniformly soft in a way real terrain, sculpted by millions of years of flowing water, does not. Hydraulic erosion simulates that weathering directly: drop thousands of virtual water droplets onto the heightmap, and let each one follow the local downhill gradient, picking up sediment where it flows fast over steep terrain and depositing it where it slows down in flatter areas.

for each of N droplets:
  pos = random point on the map
  while droplet has not stopped:
    grad = heightGradient(pos)          // steepest descent direction
    pos  += -grad * speed
    capacity = speed * water * slopeFactor
    if sediment > capacity: deposit(pos, sediment - capacity)
    else:                    erode(pos, capacity - sediment)
    speed  = update from height loss (energy conservation)
    water *= evaporation                // droplet eventually stops

Run over thousands of droplets this carves recognisable valleys and deposits fans exactly where flowing water would, entirely from a deterministic starting heightmap and a random set of drop points — no hand-authored river data required. Because it is expensive per-droplet, real-time implementations run it as an optional post-process pass rather than every frame, and generate the base fBm chunk-by-chunk as the camera pans so the world is effectively infinite without ever holding the whole map in memory at once.

Frequently asked questions

Why does terrain generated from one noise layer look unnatural?

A single noise layer only has detail at one spatial scale, so it looks like smooth, featureless rolling hills. Real terrain has structure at many scales at once — large mountains, medium ridges, small boulders — which is exactly what summing several octaves of noise at different frequencies (fBm) reproduces.

What is the difference between lacunarity and gain in fBm noise?

Lacunarity controls how much finer the frequency gets with each added octave (typically doubling); gain controls how much weaker the amplitude of each new octave is (typically halving). Together they set how much fine detail is layered on top of the broad shape, and therefore how rough or smooth the terrain looks.

Does hydraulic erosion actually simulate water physically?

It is a simplified particle-based approximation, not a full fluid solver: each droplet follows the steepest local downhill gradient and exchanges sediment based on its speed and capacity, without simulating pressure or turbulence. It is cheap enough to run interactively while still producing believable valleys and depositional fans.

Try it live

Everything above runs in your browser — open Procedural Terrain and change the parameters while it is running. Nothing is installed, nothing is uploaded, the whole model lives in one tab.

▶ Open Procedural Terrain simulation

What did you find?

Add reproduction steps (optional)