Hydraulic Erosion

Pure noise terrain looks like lumpy oatmeal. Real mountains have ridgelines, gullies, and valleys. They earned them: for a few million years, every raindrop that fell on them picked up a little dirt and dragged it downhill. We can fake the few million years in about fifteen seconds.

The demo on this page is a real simulation running in your browser, not a video. Press Run demo, then come back. Every slider in this article pokes the live terrain.

Start with noise

Every terrain here starts as a heightmap: a grid of numbers between 0 and 1, where 0 means valley floor and 1 means summit. The renderer pushes each point of the mesh up by its number.

To fill the grid we use fractal noise. Noise in this sense means smooth random bumps, not TV static. We stack several layers of it. The first layer makes a few big hills. Each layer after that adds smaller bumps on top, so the big shapes pick up fine detail. Those layers are the "octaves" below. Try the sliders. The terrain rebuilds as you drag:

One trick worth knowing: raise every height to a power. With an exponent above 1, the middle elevations sink and only the peaks stay tall. With an exponent below 1, everything puffs up into rolling plateaus.

Notice what is missing at every setting: drainage. Each point of noise is computed on its own, with no knowledge of its neighbors, so nothing connects the bumps into watersheds. That is the job of the rain.

A single raindrop

The algorithm is gloriously simple. A droplet is just five numbers: position, direction, velocity, water, and sediment. Drop it somewhere random and loop:

for life in lifetime:               # ~30 steps
    grad = gradient_at(pos)         # bilinear, sub-texel
    dir  = dir * inertia - grad * (1 - inertia)
    pos += normalize(dir)           # move one texel
    dh   = height(pos) - height(old_pos)
    # ... erode or deposit (next section) ...
    vel  = sqrt(max(0, vel² - dh * gravity))
    water *= 1 - evaporation

Press Step under the demo a few times to follow one droplet. A line traces its path, and the readout shows its water, speed, and sediment after every move. The trail starts blue while the droplet runs clean, then turns rust orange as it picks up dirt.

Inertia is the personality knob. At 0, the droplet always rolls straight downhill and zigzags into the first pit it finds. Near 1, it plows straight ahead and ignores the terrain. A small amount, around 0.05, lets it swing wide around bends the way real water does:

Carrying capacity

The heart of the model is one line. It decides how much sediment a droplet can hold:

capacity = max(-dh, min_slope) · velocity · water · capacity_factor

Fast water on a steep slope can carry a lot. Slow water on flat ground can barely carry anything. After every move, the droplet compares its load against this limit:

All of these knobs feed the same loop. Early droplets carve a channel, the channel funnels later droplets, and those droplets deepen it. That feedback is why the result looks organized rather than sandblasted.

The full storm

Now press Play and rain tens of thousands of droplets. The simulation spends a few milliseconds per frame and then yields, so the page stays smooth while the terrain erodes as fast as your machine allows. Toggle the erosion tint to see the bookkeeping: red where ground was removed, blue where it was laid down.

Things to try:

Notes & further reading

This demo simulates individual droplets, which the literature calls the Lagrangian approach. The other family is pipe-model erosion, where water sits in every cell and flows between neighbors (Eulerian). That one maps beautifully onto GPU shaders and is a likely future experiment here.

Left-drag to orbit · right-drag to pan · WASD to walk · scroll to zoom · Step traces a single droplet and zooms in · Reset re-rolls the terrain.