Fluid Dynamics Simulation

physicswasmrustwebglfluid-dynamics

Particle-Based Fluid Simulation

This simulation demonstrates real-time fluid dynamics using the double density relaxation algorithm by Clavet, Beaudoin, and Poulin (2005). The physics engine is written in Rust and compiled to WebAssembly, achieving 60fps with 3000+ particles.

Three density classes stratify naturally: heavy particles (cyan) sink, medium particles (blue) occupy the middle, and light particles (purple) float. This demonstrates realistic buoyancy as different classes separate into distinct horizontal layers.

Loading simulation...

FPS: 0
Frame: 0.0ms
Particles: 3000

Tap and drag to interact

Algorithm

The simulation implements a three-pass approach each frame following Clavet et al. with extensions for multiple density classes:

Pass 1: Forces & Position Update Apply viscosity impulses between approaching particles using the previous frame’s spatial hashmap, store previous positions for Verlet integration, apply gravity scaled by density class (1.2× heavy, 1.0× medium, 0.75× light), update positions, and rebuild the spatial hashmap for neighbor queries.

Pass 2: Double Density Relaxation Freeze positions at the start of the pass to prevent feedback loops. Calculate density (Σg²) and near-density (Σg³) for each particle using smoothing kernels. Compute pressure based on deviation from class-specific rest density, then apply symmetric pair pressure (P_i + P_j) with mass-weighted displacement splitting. Lighter particles move more than heavier ones from the same pressure differential—this is what enables stratification while preserving momentum and creating incompressibility through particle repulsion.

Pass 3: Boundary & Velocity Constrain particles to the circular container with a hysteresis threshold (0.5px dead zone) to prevent jitter, snap particles slightly inside the boundary to prevent sticking, reflect outward velocities with 80% energy retention, and calculate final velocities from position deltas.

Performance

The Rust/WASM pipeline uses zero-copy memory access via shared TypedArrays and Structure of Arrays (SoA) layout for cache coherency—all X positions stored contiguously, then all Y positions. Spatial hashing provides O(1) neighbor lookups using grid partitioning, avoiding O(n²) all-pairs complexity.

WebGL 2 rendering uses GPU-instanced drawing (single draw call for all particles) with alpha blending for consistent opacity and soft-edged particles for a blob-like appearance. Particles initialize in a Fermat spiral (sunflower pattern) for uniform distribution. The rendering pipeline handles up to 4000 particles at 60fps on modern hardware.

Key Parameters

The simulation uses constants derived from the Clavet paper with tuning for multi-density behavior:

  • Interaction Radius: 30px
  • Stiffness: 0.004/dt² ≈ 14.5 (scaled for 60fps timesteps per Clavet Algorithm 2)
  • Near Stiffness: 0.010/dt² ≈ 36.3
  • Rest Densities: Heavy=12.0, Medium=10.0, Light=8.0
  • Gravity: 200 px/s² with class-specific scaling
  • Viscosity: σ=0.02, β=0.05 (reduced to allow density separation)

Interaction

Click and drag anywhere on the canvas to apply forces to the fluid. Particles respond with realistic behaviors: incompressibility through pressure forces that maintain target density, surface tension via near-density forces that create cohesive blobs, momentum conservation obeying Newton’s third law, natural density stratification as different classes separate, and viscous damping that creates realistic fluid friction. The interaction radius and force strength are tuned to feel responsive while maintaining physical plausibility.

Implementation Notes

Several subtle issues required careful debugging to achieve accurate stratification:

Stiffness Scaling: The Clavet paper uses dt=1, but at 60fps (dt≈0.0166), stiffness must scale by 1/dt² (~3600×) to maintain equivalent pressure forces. Without this scaling, particles behave like gas instead of liquid.

Mass-Weighted Displacement: Equal 50/50 force splitting between particle pairs prevents stratification because heavy and light particles move equally. Proper inverse-mass weighting (lighter particles displace more) is essential for density classes to separate.

Update Ordering: Viscosity impulses must be applied using the previous frame’s spatial hashmap before clearing it for new positions. Clearing too early results in zero viscosity forces and unwanted mixing.

Technical Stack

  • Rust – Physics simulation with bounds checking
  • WebAssembly – Near-native browser performance
  • WebGL 2 – GPU-accelerated instanced rendering
  • React/TypeScript – UI and integration

References

Clavet, S., Beaudoin, P., & Poulin, P. (2005). Particle-based Viscoelastic Fluid Simulation. Proceedings of the 2005 ACM SIGGRAPH/Eurographics symposium on Computer animation.

The implementation follows Algorithm 2 (double density relaxation) and Algorithm 5 (viscosity impulses) with careful attention to update ordering.

This Rust/WASM implementation achieves significant performance improvements over pure JavaScript while maintaining algorithmic fidelity to the reference paper, with additional enhancements for multi-density stratification that aren’t present in the original work.