webphysics-avbd-engine Skill by ara.so — Daily 2026 Skills collection. What It Does webphysics is an experimental WebGPU-accelerated rigid-body and soft-body physics engine implementing the AVBD (Augmented Vertex Block Descent) solver from Giles et al. (2025) . It runs entirely on the GPU using WebGPU compute shaders and supports: Rigid-body simulation with contacts, friction, and joints GPU broad-phase collision detection via LBVH (Linear BVH) Narrow-phase manifold generation with warm-start persistence Graph-coloring-based parallel body solves Springs and soft-body constraints Body sleeping/diagnostics Browser support: Chrome only (requires WebGPU). This is an experimental proof-of-concept, not a production library. Installation & Setup git clone https://github.com/jure/webphysics.git cd webphysics npm install npm run dev
development server
npm run build
production build
The dev server typically starts at http://localhost:5173 (Vite-based). Project Structure src/ ├── physics/ │ ├── PhysicsEngine.ts # Main orchestration: substep loop, init, step │ └── gpu/ │ ├── avbdState.ts # Primal/dual solve, coloring, velocity finalization │ ├── broadPhase.ts # LBVH broad-phase candidate generation │ ├── contactGeneration.ts # Narrow-phase manifolds, per-body constraint lists │ ├── contactRecord.ts # Warm-start state persistence │ └── avbdState.ts # Inertial targets, primal init, iteration ├── lvbh/ │ └── GPULBVHBuilder.ts # GPU LBVH construction └── ... Core API Usage Initializing the Physics Engine import { PhysicsEngine } from './src/physics/PhysicsEngine' ; // Requires an existing GPUDevice const adapter = await navigator . gpu . requestAdapter ( ) ; const device = await adapter . requestDevice ( ) ; const engine = new PhysicsEngine ( device ) ; await engine . init ( ) ; Adding Rigid Bodies // Add a static ground plane engine . addBody ( { type : 'box' , position : [ 0 , - 1 , 0 ] , rotation : [ 0 , 0 , 0 , 1 ] , // quaternion [x, y, z, w] halfExtents : [ 10 , 0.5 , 10 ] , mass : 0 , // 0 = static/infinite mass restitution : 0.3 , friction : 0.5 , } ) ; // Add a dynamic rigid box engine . addBody ( { type : 'box' , position : [ 0 , 5 , 0 ] , rotation : [ 0 , 0 , 0 , 1 ] , halfExtents : [ 0.5 , 0.5 , 0.5 ] , mass : 1.0 , restitution : 0.2 , friction : 0.6 , } ) ; Stepping the Simulation const TIMESTEP = 1 / 60 ; const SUBSTEPS = 10 ; function gameLoop ( dt : number ) { engine . step ( dt , SUBSTEPS ) ; // Read back positions for rendering const bodyStates = engine . getBodyStates ( ) ; renderBodies ( bodyStates ) ; requestAnimationFrame ( gameLoop ) ; } requestAnimationFrame ( gameLoop ) ; Reading Body State for Rendering // After engine.step(), retrieve updated transforms const states = engine . getBodyStates ( ) ; for ( const state of states ) { const { position , rotation , bodyIndex } = state ; // position: [x, y, z] // rotation: quaternion [x, y, z, w] updateMeshTransform ( bodyIndex , position , rotation ) ; } Adding Joints / Constraints // Distance joint between two bodies engine . addJoint ( { type : 'distance' , bodyA : 0 , bodyB : 1 , anchorA : [ 0 , 0.5 , 0 ] , // local-space anchor on body A anchorB : [ 0 , - 0.5 , 0 ] , // local-space anchor on body B restLength : 1.0 , stiffness : 1e4 , } ) ; Adding Springs (Soft Bodies) engine . addSpring ( { bodyA : 2 , bodyB : 3 , anchorA : [ 0 , 0 , 0 ] , anchorB : [ 0 , 0 , 0 ] , restLength : 0.8 , stiffness : 500 , damping : 10 , } ) ; AVBD Pipeline Reference The solver follows Algorithm 1 from the AVBD paper: 1. collision detection (x^t) ↓ 2. broad phase (LBVH) → src/lvbh/GPULBVHBuilder.ts ↓ 3. narrow phase + warm start → src/physics/gpu/contactGeneration.ts ↓ 4. per-body constraint lists → src/physics/gpu/avbdState.ts ↓ 5. graph coloring → src/physics/gpu/avbdState.ts ↓ 6. inertial target y, primal init, warm-start α/γ ↓ 7. [loop] colored primal body solve (approx Hessian) ↓ 8. [loop] dual + stiffness update ↓ 9. finalize velocities Key files per stage: Stage File Orchestration src/physics/PhysicsEngine.ts Broad phase src/physics/gpu/broadPhase.ts Narrow phase src/physics/gpu/contactGeneration.ts Contact records src/physics/gpu/contactRecord.ts AVBD solve src/physics/gpu/avbdState.ts LBVH builder src/lvbh/GPULBVHBuilder.ts Configuration Patterns Solver Parameters // Passed during engine construction or step engine . step ( dt , substeps , { gravity : [ 0 , - 9.81 , 0 ] , iterations : 10 , // AVBD inner iterations per substep restitutionThreshold : 1.0 , } ) ; Tuning Stability Increase substeps (e.g., 20) for stiff stacks or fast-moving bodies Increase iterations for better constraint convergence Use mass: 0 for static bodies (never moves, acts as infinite mass) Lower stiffness values for softer, more stable joints Set restitution: 0 + high friction for non-bouncy stacking Common Patterns Stack of Boxes const groundIndex = engine . addBody ( { type : 'box' , position : [ 0 , 0 , 0 ] , halfExtents : [ 5 , 0.25 , 5 ] , mass : 0 , friction : 0.7 , restitution : 0.1 , } ) ; for ( let i = 0 ; i < 8 ; i ++ ) { engine . addBody ( { type : 'box' , position : [ 0 , 0.5 + i * 1.05 , 0 ] , halfExtents : [ 0.5 , 0.5 , 0.5 ] , mass : 1.0 , friction : 0.5 , restitution : 0.1 , } ) ; } Pendulum Chain with Distance Joints let prevIndex = engine . addBody ( { type : 'box' , position : [ 0 , 5 , 0 ] , halfExtents : [ 0.1 , 0.1 , 0.1 ] , mass : 0 , friction : 0 , restitution : 0 , } ) ; for ( let i = 1 ; i <= 5 ; i ++ ) { const curr = engine . addBody ( { type : 'box' , position : [ 0 , 5 - i , 0 ] , halfExtents : [ 0.15 , 0.15 , 0.15 ] , mass : 1.0 , friction : 0.1 , restitution : 0 , } ) ; engine . addJoint ( { type : 'distance' , bodyA : prevIndex , bodyB : curr , anchorA : [ 0 , - 0.15 , 0 ] , anchorB : [ 0 , 0.15 , 0 ] , restLength : 0.7 , stiffness : 1e5 , } ) ; prevIndex = curr ; } Integrate with Three.js Rendering import * as THREE from 'three' ; const meshes : THREE . Mesh [ ] = [ ] ; function syncPhysicsToRender ( ) { const states = engine . getBodyStates ( ) ; states . forEach ( ( state , i ) => { if ( ! meshes [ i ] ) return ; meshes [ i ] . position . set ( ... state . position ) ; meshes [ i ] . quaternion . set ( state . rotation [ 0 ] , state . rotation [ 1 ] , state . rotation [ 2 ] , state . rotation [ 3 ] ) ; } ) ; } function animate ( ) { engine . step ( 1 / 60 , 10 ) ; syncPhysicsToRender ( ) ; renderer . render ( scene , camera ) ; requestAnimationFrame ( animate ) ; } Troubleshooting WebGPU Not Available Error: navigator.gpu is undefined Only Chrome 113+ supports WebGPU by default Enable via chrome://flags/#enable-unsafe-webgpu on older versions Firefox/Safari do not currently support WebGPU Simulation Explodes / Bodies Flying Off Reduce timestep or increase substeps Lower joint stiffness values Ensure static bodies have mass: 0 Check that halfExtents are positive and non-zero Bodies Sinking Through Ground Increase iterations (try 15–20) Increase substeps Check collision shape sizing matches visual mesh Performance Issues This is a Chrome-only WebGPU project; GPU driver issues can cause slowdowns Reduce body count or iteration count Check chrome://gpu to ensure hardware acceleration is active Build Errors
Ensure Node.js >= 18
node --version
Clear cache
rm -rf node_modules dist npm install npm run build Limitations & Roadmap Notes Chrome only — no Firefox/Safari support yet Not a drop-in npm package; must clone and integrate manually Double-buffered position updates (for same-color conflict safety) not yet implemented — current path uses in-place colored body solve in avbdState.ts Experimental API — breaking changes expected No TypeScript type declarations exported for external use yet References Live Demo AVBD Paper (Giles et al., SIGGRAPH 2025) WebGPU Spec