| name | webphysics-avbd-engine |
| description | WebGPU rigid-body/soft-body physics engine based on the AVBD (Augmented Vertex Block Descent) solver |
| triggers | ["add physics simulation to my WebGPU project","set up webphysics engine","implement rigid body physics in the browser","use AVBD solver for collision detection","WebGPU physics simulation setup","add collision detection with webphysics","configure physics bodies and constraints","run GPU accelerated physics in TypeScript"] |
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
npm run 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';
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const engine = new PhysicsEngine(device);
await engine.init();
Adding Rigid Bodies
engine.addBody({
type: 'box',
position: [0, -1, 0],
rotation: [0, 0, 0, 1],
halfExtents: [10, 0.5, 10],
mass: 0,
restitution: 0.3,
friction: 0.5,
});
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);
const bodyStates = engine.getBodyStates();
renderBodies(bodyStates);
requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);
Reading Body State for Rendering
const states = engine.getBodyStates();
for (const state of states) {
const { position, rotation, bodyIndex } = state;
updateMeshTransform(bodyIndex, position, rotation);
}
Adding Joints / Constraints
engine.addJoint({
type: 'distance',
bodyA: 0,
bodyB: 1,
anchorA: [0, 0.5, 0],
anchorB: [0, -0.5, 0],
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
engine.step(dt, substeps, {
gravity: [0, -9.81, 0],
iterations: 10,
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
node --version
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