Spatial hashing, parallel passes, and scaling from dozens to thousands of particles.
Part 2 of Smooth Particle Hydrodynamics
In Part 1 we built a fluid simulation that computes density, pressure, and viscosity for each particle by looking at its neighbors. The physics was the interesting part. But the expensive part is something much simpler: finding who those neighbors are.
For each particle, we scan every other particle and check whether it falls within the smoothing radius. With n particles, that means n × (n − 1) distance checks per frame. Double the particle count, quadruple the work.
At 100 particles, that's 9,900 checks. At 1,000, it's 999,000. The physics formulas from Part 1 are cheap per pair. It's the sheer number of pairs that bogs things down. We need a way to skip the particles that are obviously too far away.
The idea is straightforward: divide the simulation space into a grid of cells, each sized to match the smoothing radius H. Each particle drops into the cell that contains its position. When we need a particle's neighbors, we only check the 9 cells surrounding it (a 3×3 block in 2D) instead of every particle in the simulation.
With uniformly distributed particles, the spatial hash reduces neighbor search from O(n²) to roughly O(n). Each particle only checks a handful of candidates instead of the entire population.
On the CPU, we loop through particles one at a time. On the GPU, every particle runs the same computation simultaneously. The inner loop (checking neighbors) is the same. The outer loop disappears.
Once we have an efficient neighbor search, the next observation is that each particle's computation within a single pass is independent. Particle 42's density doesn't depend on what particle 17 computed for its density in the same step. This makes SPH a natural fit for GPU parallelism: launch one thread per particle, all running the same code simultaneously.
The simulation loop from Part 1 becomes a sequence of GPU dispatches. Each pass reads from buffers written by the previous pass and writes its own results. The diagram below shows how data flows through the four passes.
This simulation runs on your GPU using WebGPU compute shaders. The same four passes from the pipeline diagram above, each dispatching one thread per particle.
The GPU runs thousands of particles in parallel, each thread executing the same density/force/integration code. The spatial hash keeps the neighbor search cheap, and the parallelism handles the rest. In Part 3 we explore what we can do with that headroom: springs, friction, temperature, and collisions with solid objects.