Task 1 of 6

Seeing the Ground

A heightmap is a grid of numbers, and nobody can judge a landscape from numbers. Before we start moving rock around we need eyes: a picture in which a valley looks like a valley. The standard one is a hillshade — pretend the terrain is a solid surface, work out which way each cell faces, and see how squarely it faces the sun.

Which way a cell faces is its slope, and slope on a grid is a central difference: the neighbour to the right minus the neighbour to the left, halved because that step spans two cells. (The same two-tap derivative the Sobel filters use — this is just the one place it gets to be lighting instead of edges.) With gx and gy in hand the surface normal is (−gx, −gy, 1); normalise it, dot it with the light ℓ = (−1, −1, 1)/√3, and the whole thing collapses to

lit = 0.5774 * (1 + gx + gy)
    / Math.sqrt(gx * gx + gy * gy + 1)

The grid wraps at the edges, the way Reaction–Diffusion's did: column 0's left neighbour is the last column. No special cases, no missing pixels.

two neighbours make a slope, a slope makes a normal, a normal catches the light
Goal: finish the graphical kernel so it hillshades terrain — central differences for gx and gy, then the lit value above, clamped at zero.

Requirements

Hint 1 — the two slopes

The wrapped indexes are already computed for you:

const gx = (height[y][xr] - height[y][xl])
  * 0.5 * this.constants.relief;
const gy = (height[yu][x] - height[yd][x])
  * 0.5 * this.constants.relief;

relief is only a vertical exaggeration — terrain this shallow would be almost invisible at a true aspect ratio.

Hint 2 — the rest of it
const inv = 1 / Math.sqrt(gx * gx + gy * gy + 1);
let lit = this.constants.light * (1 + gx + gy) * inv;
if (lit < 0) lit = 0;

The palette line underneath is already written; it only wants lit.

Same idea elsewhere

Hillshading is a fragment shader that has escaped its renderer: one texture read per neighbour, one dot product, one write. Every terrain engine — WebGPU, Metal, Unreal's landscape — does exactly this, usually with the normals baked into a texture beside the heights so the fetch is one sample instead of four.

All tasks in Hydraulic Erosion: Carving Terrain by Accumulation

  1. Seeing the Ground
  2. Which Way Is Down
  3. Everybody Downhill At Once
  4. Capacity: What the Water Can Carry
  5. Ping-Pong the Whole Landscape
  6. Two Hundred Steps, and a River Appears

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.