Real-time, pose-driven fluid simulation in the browser. Move your body to generate smoke that reacts to your gestures.
Built on Jos Stam's Stable Fluids algorithm with Harmonic Function Mapping for localized body-to-fluid interaction and Simplex Noise for organic turbulence.
This project implements a GPU-accelerated Navier-Stokes solver based on the following foundational work:
Jos Stam. "Stable Fluids." SIGGRAPH 1999 Proceedings, pp. 121-128.
The core simulation uses Stam's unconditionally stable method for solving the incompressible Navier-Stokes equations in real time. The key insight is replacing explicit advection with a Semi-Lagrangian scheme that traces particles backward through the velocity field, guaranteeing stability at any time step.
Each simulation frame follows these steps:
- Advection — Trace velocity and density fields backward through the current velocity field (Semi-Lagrangian with optional BFECC correction for reduced numerical dissipation)
- External Forces — Inject forces from body tracking, harmonic oscillation, curl noise, and buoyancy
- Diffusion — Solve viscous diffusion via iterative Jacobi relaxation
- Projection — Enforce incompressibility by computing the pressure field (Poisson equation via Jacobi iteration) and subtracting its gradient from the velocity field
All steps run as WebGL fragment shaders on the GPU.
Standard noise-based turbulence is global and directionless. This project introduces Harmonic Function Mapping to generate localized, directional force fields around the user's skeleton:
- Interpolation — For each pixel, compute projection point and parameter t along skeleton segments
- Direction — Calculate the vector from the segment toward the current pixel
- Oscillation — Apply an adjusted harmonic function H(x) = (cos(x) · sin(4x) + 2) / 2, shifted to remain non-negative, preventing force reversal
- Attenuation — Gaussian falloff ensures forces decay smoothly with distance from the skeleton
This produces structured, body-responsive turbulence that global noise methods cannot achieve.
Harmonic Function Mapping: interpolation on skeleton segments with harmonic oscillation and Gaussian attenuation
- Jos Stam, "Stable Fluids", SIGGRAPH 1999
- Jos Stam, "Real-Time Fluid Dynamics for Games", GDC 2003
- Stefan Gustavson, "Simplex noise demystified", 2005
- MediaPipe Pose Landmarker, Google
Data flow from webcam input to final rendering
- Webcam Stream — Captures real-time video from the user's device
- Pose Detection — MediaPipe infers 33 joint landmarks in a Web Worker (decoupled from the render loop)
- Force Generation — Body data is converted to GPU force fields via Harmonic Mapping + Simplex Noise
- Fluid Solver — Velocity and density fields updated each frame on the GPU
- Rendering — Density mapped to color via customizable fragment shaders (aurora, ocean, heatmap palettes)
- Body-driven interaction — Fluid responds to hand direction, arm speed, and full-body movement
- Real-time GPU simulation — Stable Fluids solver running entirely in WebGL shaders
- Async pose tracking — Web Worker architecture prevents UI jank
- AR composition — Overlay fluid on live webcam feed
- Customizable visuals — Multiple color palettes, adjustable parameters via GUI
- No plugins — Runs in any modern browser (Chrome, Firefox, Edge)
AR composition: fluid overlaid on live webcam
- Modern browser with WebGL (Chrome, Firefox, Edge)
- Webcam (optional — sample video fallback included)
git clone https://github.com/ellenlivia-mem0/flowground.git
cd flowground
npm install
npm run devOpen http://localhost:3001 and allow camera access (or choose the sample video option).
The GUI panel (bottom-right, click to expand) exposes all simulation parameters:
| Parameter | Description |
|---|---|
isMouse |
Switch between mouse and webcam interaction |
osc_strength |
Harmonic oscillation force intensity |
n_strength |
Background Simplex Noise turbulence |
buoyancy |
Upward force on the fluid |
mouse_force |
External force magnitude (mouse mode) |
cursor_size |
Radius of force/smoke emission |
isViscous |
Enable viscous diffusion |
viscous |
Viscosity coefficient |
dt |
Simulation time step |
BFECC |
Back and Forth Error Compensation (sharper details) |
- Simulation — WebGL / GLSL fragment shaders
- Pose Tracking — MediaPipe Pose Landmarker
- Rendering — Three.js
- Async — Web Workers
- UI — Tailwind CSS, dat.GUI
- Build — Webpack, Babel
Camera access requires a secure context (https:// or localhost).
| Environment | Works? |
|---|---|
file:// (opening HTML directly) |
No |
http:// (non-localhost) |
No |
https:// |
Yes |
http://localhost |
Yes |
If you previously denied camera access, click the lock icon in the URL bar to reset permissions and reload.
MIT

