Home▸Fluid Dynamics & Aerodynamics▸Real-Time Fluid Simulation (2D) — CPU Navier-Stokes Solver

Real-Time Fluid Simulation (2D) — CPU Navier–Stokes Solver

A plain-arrays companion to the GPU fluid solver: the same Jos Stam "Stable Fluids" equations (diffuse → project → advect) run on the CPU with a visible Gauss-Seidel relaxation loop instead of GLSL fragment shaders, so you can trade grid resolution for solver speed and see exactly what each stage does.

Fluid Dynamics & Aerodynamics2DModerate60 FPS⇄ 3D version
2d-fluid-simulation ↗ Open standalone

This 2D companion strips away the GPU/GLSL layer of the WebGL original and runs Jos Stam's "Stable Fluids" scheme directly on JavaScript typed arrays: an explicit Gauss-Seidel relaxation loop you can watch converge, rather than a fragment-shader Jacobi sweep. Drag anywhere on the grid to inject velocity and dye, then use the Viscosity, Dye diffusion and Fade sliders to reshape how momentum and colour spread and decay. A Grid resolution slider lets you trade detail for solver speed (48×48 up to 88×88), and the on-screen Solver time readout shows the real per-frame cost of the CPU Gauss-Seidel iterations — something the GPU version, running the same maths in parallel, never has to expose.

⚙ Under the hood

CPU port of Jos Stam's unconditionally-stable Navier–Stokes solver: diffuse → project (pressure Poisson solve via Gauss-Seidel) → advect (semi-Lagrangian backtrace) for velocity, then diffuse → advect → fade for the dye field, each frame, on a resizable interior grid.

navier-stokesfluid dynamicsgauss-seidelstable fluidsadvectiondiffusion

2D · HTML5 Canvas 2D · 60 FPS target · runs fully client-side, no install

Frequently Asked Questions

How is this different from the 3D/WebGL version?

Same equations, different hardware target. The 3D version runs every solver stage as a GLSL fragment shader on GPU render targets, so all cells update in parallel. This 2D version runs the identical add-force / diffuse / project / advect stages as plain nested `for` loops over Float32Array grids on the CPU, which is why it exposes a resolution slider and a solver-time readout — the cost of each Gauss-Seidel sweep is directly visible instead of hidden behind the GPU.

What do Viscosity, Dye diffusion and Fade control?

Viscosity smooths the velocity field (internal friction, the ν∇²u term); Dye diffusion spreads the visible colour independently of the flow; Fade multiplies the dye field by a factor each frame so injected colour eventually disappears instead of accumulating forever.

Why does raising the grid resolution slow the simulation down?

The Gauss-Seidel diffusion and pressure solves are run 16 times per frame over every interior cell, so cost scales roughly with the number of cells (N²). Going from 48×48 to 88×88 more than triples the cell count and the per-frame solver time along with it — visible directly in the Solver time readout.

What did you find?

Add reproduction steps (optional)