# Everybody Downhill At Once

*Task 3 of 6 · [Hydraulic Erosion: Carving Terrain by Accumulation](https://gpu.rocks/learn/hydraulic-erosion-07165ca1.md) · GPU.js Learn*

Now move the water. On a CPU you would walk each cell and *push* its water
into the neighbours below it. You cannot do that here: two cells pushing into the same
neighbour is a write collision, and a kernel writes exactly one output cell — the whole
point Thinking in Parallel makes. So invert it. Each cell works out what its uphill
neighbours would have **sent** it, and adds that up.

Neighbour `n` hands this cell the share `d² / drop[n]` of its
water. And there is the catch that shapes the whole module: `drop[n]` is an
aggregate over *n*'s neighbours, which are two rings away from us. A gather cannot
ask a neighbour to add something up on demand — so the previous task exists purely to
have that total already sitting in a grid we can read. **When a gather needs a
neighbour's aggregate, the aggregate gets its own pass.**

The cell also hands its own water on, `move` of it per step, and keeps
`1 − move·drop/(drop + soft)`. That keep-weight is the centre weight of an
explicit diffusion step wearing a different hat, and it obeys the same rule
The Heat Equation & Stability derives: let it go negative — here, take
`move` past `1` — and the grid detonates. The
`+ soft` keeps the division safe where a cell has nowhere to go, and quiets
flat water, which would otherwise flip back and forth between neighbours forever.

## Figures

- **the total lives two rings away, so it gets a pass of its own**

## Goal

**Goal:** complete `moveWater` — gather each uphill
neighbour's share, subtract what this cell hands on, and add the rain.

## Requirements

- For each neighbour above this cell, add `d * d * water[n] / (drop[n] + soft)` — the neighbour's drop total, not this cell's
- Subtract `given = drop[here] * water[here] / (drop[here] + soft)`
- Return `water + move * (taken − given) + rain`

## Hint 1 — whose total is it?

The share is *the neighbour's*: it is dividing up *the neighbour's*
water, so the denominator has to be the neighbour's drop total.

```js
d = height[y][xl] + water[y][xl] - here;
if (d > 0) taken += (d * d * water[y][xl]) / (drop[y][xl] + this.constants.soft);
```

Divide by `drop[y][x]` instead and the shares stop summing to one — water
appears and vanishes.

## Hint 2 — the outgoing half

Sum the shares this cell gives away and you get `drop[y][x]` back on
top, so the whole outgoing side is one line:

```js
const given = (drop[y][x] * water[y][x]) / (drop[y][x] + this.constants.soft);
return water[y][x] + this.constants.move * (taken - given) + this.constants.rain;
```

## Hint 3 — the test that catches everything

Water is conserved: whatever leaves one cell arrives in another, so after one
step the grid holds exactly what it held plus one step of rain. If your total drifts,
the two halves are not using the same shares.

## Same idea elsewhere

Gather-instead-of-scatter with a pre-computed normaliser is the shape of sparse
matrix–vector multiply, of graph message passing, and of every particle-to-grid transfer
written for a GPU: nobody writes to a neighbour, everybody reads from one, and any
per-source total the readers need is materialised by an earlier pass.

## Starter code

```js
// Nobody pushes. Everybody pulls.
const gpu = new GPU({ mode });

const dropTotal = gpu.createKernel(function (height, water) {
  const x = this.thread.x;
  const y = this.thread.y;
  let xl = x - 1; if (xl < 0) xl = this.constants.size - 1;
  let xr = x + 1; if (xr > this.constants.size - 1) xr = 0;
  let yd = y - 1; if (yd < 0) yd = this.constants.size - 1;
  let yu = y + 1; if (yu > this.constants.size - 1) yu = 0;
  const here = height[y][x] + water[y][x];
  let total = 0;
  let d = here - (height[y][xl] + water[y][xl]); if (d > 0) total += d * d;
  d = here - (height[y][xr] + water[y][xr]); if (d > 0) total += d * d;
  d = here - (height[yd][x] + water[yd][x]); if (d > 0) total += d * d;
  d = here - (height[yu][x] + water[yu][x]); if (d > 0) total += d * d;
  return total;
}, { output: [64, 64], constants: { size: 64 } });

const moveWater = gpu.createKernel(function (height, water, drop) {
  const x = this.thread.x;
  const y = this.thread.y;
  let xl = x - 1; if (xl < 0) xl = this.constants.size - 1;
  let xr = x + 1; if (xr > this.constants.size - 1) xr = 0;
  let yd = y - 1; if (yd < 0) yd = this.constants.size - 1;
  let yu = y + 1; if (yu > this.constants.size - 1) yu = 0;
  const here = height[y][x] + water[y][x];

  let taken = 0;
  let d = height[y][xl] + water[y][xl] - here;
  if (d > 0) taken += (d * d * water[y][xl]) / (drop[y][xl] + this.constants.soft);
  // TODO: the other three neighbours — xr, yd, yu

  // TODO: how much does this cell hand on? and don't forget the rain.
  return water[y][x] + this.constants.move * taken;
}, {
  output: [64, 64],
  constants: { size: 64, move: 0.6, soft: 0.0002, rain: 0.00002 },
});

const drop = await dropTotal(terrain, water);
const next = await moveWater(terrain, water, drop);

let before = 0;
let after = 0;
for (let y = 0; y < 64; y++) {
  for (let x = 0; x < 64; x++) { before += water[y][x]; after += next[y][x]; }
}
console.log('water before:', before, ' after:', after, ' rain added:', 64 * 64 * 0.00002);
```

---

Interactive version: https://gpu.rocks/learn/hydraulic-erosion-07165ca1/3

[Previous task](https://gpu.rocks/learn/hydraulic-erosion-07165ca1/2.md) · [Next task](https://gpu.rocks/learn/hydraulic-erosion-07165ca1/4.md)
