Verlet

Verlet integration for ropes and cloth

Quick Start

import { createVerlet } from './webgpu-market/verlet/verlet';

const rope = createVerlet(device, {
  points: 32,
  gravity: [0, 9.8],
  damping: 0.99,
  pins: [0], // pin the first point
});

// Each frame
rope.update(deltaTime);

// Bind rope.positions (GPUBuffer of vec2f) in your render pipeline
rope.destroy();
Source
// Verlet position integration
// Computes new positions using: new_pos = pos + (pos - old_pos) * damping + acceleration * dt^2
// Pinned points are skipped (their positions remain fixed).

struct Uniforms {
  dt: f32,
  damping: f32,
  gravity_x: f32,
  gravity_y: f32,
  point_count: u32,
  bounds_width: f32,
  bounds_height: f32,
  _pad: u32,
}

@group(0) @binding(0) var<uniform> u: Uniforms;
@group(0) @binding(1) var<storage, read_write> positions: array<vec2f>;
@group(0) @binding(2) var<storage, read_write> old_positions: array<vec2f>;
@group(0) @binding(3) var<storage, read> pins: array<u32>;

@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) gid: vec3u) {
  let idx = gid.x;
  if (idx >= u.point_count) {
    return;
  }

  // Pinned points don't move — keep old_pos in sync so they have zero velocity
  if (pins[idx] == 1u) {
    old_positions[idx] = positions[idx];
    return;
  }

  let pos = positions[idx];
  let old_pos = old_positions[idx];

  // Verlet integration: velocity is implicit as (pos - old_pos)
  let velocity = (pos - old_pos) * u.damping;
  let acceleration = vec2f(u.gravity_x, u.gravity_y);
  let dt_sq = u.dt * u.dt;

  var new_pos = pos + velocity + acceleration * dt_sq;

  // Boundary constraints — keep points within the simulation area.
  // Bounds are centered at origin.
  let hw = u.bounds_width * 0.5;
  let hh = u.bounds_height * 0.5;

  if (hw > 0.0 && hh > 0.0) {
    new_pos.x = clamp(new_pos.x, -hw, hw);
    new_pos.y = clamp(new_pos.y, -hh, hh);
  }

  old_positions[idx] = pos;
  positions[idx] = new_pos;
}
Documentation

Verlet

Verlet integration for ropes, cloth, and soft bodies. Two compute shaders handle position integration and distance constraint solving. Rendering is separate.

API

createVerlet(device, options)

Returns a Verlet instance.

Option Type Default Description
points number (required) Number of points
constraints VerletConstraint[] (rope chain) Pairs of point indices + rest lengths
gravity [number, number] [0, 9.8] Acceleration applied each frame
damping number 0.99 Velocity damping (0 = frozen, 1 = no damping)
bounds { width, height } (none) Boundary box centered at origin
pins number[] [0] Indices of fixed (immovable) points
constraintIterations number 8 Constraint relaxation passes per frame

Each VerletConstraint has:

Field Type Description
a number Index of the first point
b number Index of the second point
restLength number Target distance between the points

sim.update(deltaTime)

Advances the simulation by deltaTime seconds. Runs one integration pass, then constraintIterations constraint relaxation passes. Delta time is clamped to 1/30s to prevent instability.

sim.positions

GPUBuffer containing count vec2<f32> values (current positions). Usages: STORAGE | COPY_DST | VERTEX | COPY_SRC.

sim.count

Number of points in the simulation.

sim.destroy()

Releases all GPU buffers.

Further Reading

Further Reading

Resources on Verlet integration, position-based dynamics, and constraint-based physics simulation.

Original Research

  • Loup Verlet, "Computer 'Experiments' on Classical Fluids" (Physical Review, 1967) The original paper introducing the Verlet integration method for molecular dynamics simulation. The position-based formulation (Stormer-Verlet) is what this module implements. https://doi.org/10.1103/PhysRev.159.98

  • Jakobsen, "Advanced Character Physics" (GDC 2001) The landmark talk that popularized Verlet integration for real-time game physics. Introduces the pattern of Verlet integration + iterative constraint relaxation for ropes, cloth, and ragdolls. This is the direct basis for this module's approach. https://www.researchgate.net/publication/228599597_Advanced_character_physics

  • Muller et al., "Position Based Dynamics" (2007) Formalizes the Verlet + constraint projection approach into the PBD framework. Covers distance constraints, volume preservation, collision handling, and the relationship between iteration count and stiffness. https://matthias-research.github.io/pages/publications/posBasedDyn.pdf

Extended Methods

Cloth Simulation

GPU Implementation

  • Tassone, Cozzi, "A Parallel Constraint Solver for a Deformable Body Simulation" (2006) Discusses GPU parallelization strategies for constraint solving, including the graph coloring approach to avoid write conflicts between constraints that share points.

  • NVIDIA Flex A unified GPU particle physics engine built on PBD, handling cloth, fluids, rigid bodies, and soft bodies. Good reference for how Verlet-based simulation scales to complex scenes. https://developer.nvidia.com/flex

Practical Guides

  • Daniel Shiffman, "The Nature of Code" — Chapter on Springs Accessible introduction to spring-based particle systems, covering the basics of Verlet integration and constraint solving with step-by-step code examples. https://natureofcode.com/oscillation/#spring-forces

  • Matthias Muller, "Ten Minute Physics" (YouTube) Video series by the PBD author covering practical implementation of position-based simulation, including cloth, soft bodies, and fluids. https://www.youtube.com/c/TenMinutePhysics

General References

  • Hairer, Lubich, Wanner, "Geometric Numerical Integration" (2006) Rigorous mathematical treatment of symplectic integrators including Verlet/Stormer-Verlet. Explains why Verlet integration conserves energy better than Euler methods over long simulations.

  • Bridson, "Fluid Simulation for Computer Graphics" (2015) While focused on fluids, includes excellent coverage of particle-based simulation, time integration, and constraint methods that apply broadly to Verlet-based systems.