A real-time interactive fluid dynamics simulation running entirely in the browser. Move your mouse or touch the screen to inject colourful dye into a GPU-driven fluid, creating swirling vortices and turbulence that gradually settle to rest.
This is a basic spike/stripped-back version heavily inspired by Pavel Dobryakov's WebGL Fluid Simulation — definitely check out the original as it is very good and has many more features.
The simulation solves an approximation of the Navier-Stokes equations on the GPU using WebGL fragment shaders. Each frame runs a multi-step pipeline:
- Curl — compute the curl of the velocity field
- Vorticity Confinement — amplify small-scale rotational detail that numerical dissipation would otherwise destroy
- Advection — move the velocity field through itself (self-advection)
- Divergence — calculate how much fluid is expanding/compressing at each point
- Pressure Solve — iteratively solve for pressure using Jacobi iteration (30 iterations per frame)
- Gradient Subtraction — subtract the pressure gradient from velocity to enforce incompressibility
- Dye Advection — carry the visible colour field along with the velocity
User interaction (mouse/touch) injects velocity impulses and randomly coloured dye via a Gaussian splat into the simulation fields.
- Plain HTML/CSS/JS — no build step, no frameworks, no dependencies
- WebGL 1.0 with
OES_texture_half_float/OES_texture_floatextensions - GLSL fragment shaders loaded at runtime via
fetch() - Double-buffered framebuffer objects (ping-pong) for read/write separation
FluidSim/
├── index.html # Minimal page with a full-screen <canvas>
├── css/
│ └── style.css # Full-bleed canvas, no scrollbars, touch-action: none
├── js/
│ └── fluid.js # WebGL setup, shader compilation, input handling, render loop
└── shaders/
├── baseVertex.vert # Shared full-screen quad vertex shader
├── advection.frag # Advects velocity & dye fields
├── curl.frag # Computes velocity curl
├── vorticity.frag # Vorticity confinement force
├── splat.frag # Injects colour + velocity at interaction point
├── divergence.frag # Computes velocity divergence
├── pressure.frag # Jacobi pressure solve iteration
├── gradient.frag # Subtracts pressure gradient from velocity
└── display.frag # Final output / colour mapping
No build or install required. Serve the files with any static HTTP server:
# Python
python -m http.server 8000
# Node.js (npx)
npx serve .Then open http://localhost:8000 in a modern browser and move your mouse over the canvas.
Note: The files must be served over HTTP(S) — opening
index.htmldirectly viafile://will not work because the shaders are loaded withfetch().
Key constants at the top of js/fluid.js:
| Constant | Default | Description |
|---|---|---|
SIM_RESOLUTION |
256 | Simulation grid size (velocity/pressure) |
DYE_RESOLUTION |
1024 | Dye field resolution (visual output) |
SPLAT_RADIUS |
0.25 | Size of each colour/velocity injection |
SPLAT_FORCE |
4000 | Strength of velocity impulse on interaction |
VISCOSITY |
0.8 | Rate at which velocity dissipates |
PRESSURE_ITERATIONS |
30 | Jacobi solver iterations per frame |
CURL_STRENGTH |
20 | Vorticity confinement intensity |
Modern evergreen browsers on desktop and mobile (Chrome, Firefox, Safari, Edge). Requires WebGL 1.0 with float texture support. The canvas honours devicePixelRatio (capped at 2×) for crisp HiDPI rendering.