Home Games Shader Sandbox

Game Dev Mechanics: Verlet Integration — How It Works

Verlet Integration
Cloth Simulation
Click & drag to pull cloth  |  Wind simulation active

In game development, simulating realistic physical behaviour — cloth rippling in wind, a rope swinging between two anchor points, a ragdoll tumbling down a staircase — is a fundamentally hard problem. The naive approach of computing forces, accelerations, and velocities leads to numerical instability: simulations that explode, oscillate wildly, or demand extremely small time steps to remain usable. Verlet integration is one of the most important techniques in real-time game physics.

What Is Verlet Integration?

Named after French physicist Loup Verlet, who introduced it in 1967 for molecular dynamics simulations, Verlet integration is a numerical method for solving Newton's equations of motion. Unlike traditional Euler integration — which tracks both position and velocity as separate, explicit state variables — Verlet integration derives velocity implicitly from the difference between the current and previous positions of a particle.

The core insight is simple. If you know where a particle was last frame and where it is this frame, you can infer how fast it is moving without storing a velocity vector at all. And if you know that implied velocity plus the current acceleration, you can predict where it will be next frame.

The Problem With Euler Integration

To appreciate why Verlet matters, consider the standard semi-implicit Euler method used in many introductory physics engines:

velocity += acceleration * dt;
position += velocity * dt;

This is easy to understand and fast to execute, but it has a critical flaw: it is a first-order method, meaning truncation errors accumulate at a rate proportional to $\Delta t$. In practice, a spring simulated with Euler integration gradually gains energy with each step until the object bounces wildly and the simulation diverges. This is why game developers using Euler often resort to very small fixed timesteps, aggressive artificial damping, or special-case hacks to keep things stable.

The Position Verlet Formula

Position Verlet replaces the explicit velocity with an implicit one stored as position history. The update rule is:

$$\vec{x}_{n+1} = 2\vec{x}_n - \vec{x}_{n-1} + \vec{a}_n \cdot \Delta t^2$$

Where:

  • $\vec{x}_{n+1}$ is the position we are computing for the next frame
  • $\vec{x}_n$ is the current position
  • $\vec{x}_{n-1}$ is the previous frame's position
  • $\vec{a}_n$ is the current acceleration (net force divided by mass)
  • $\Delta t$ is the timestep duration

The term $2\vec{x}_n - \vec{x}_{n-1}$ is key. It can be rewritten as $\vec{x}_n + (\vec{x}_n - \vec{x}_{n-1})$, which is simply the current position plus the displacement vector from last frame — the implied velocity. Damping is trivially applied by scaling that displacement before adding it:

$$\vec{x}_{n+1} = \vec{x}_n + (\vec{x}_n - \vec{x}_{n-1}) \cdot d + \vec{a}_n \cdot \Delta t^2$$

Where $d$ is a damping coefficient slightly less than 1.0 (e.g., 0.985). This naturally dissipates energy at a controlled rate without requiring any separate velocity damping pass.

In code, this becomes three lines per particle:

function integrateVerlet(point, gravity, damping) {
    const vx = (point.x - point.px) * damping;
    const vy = (point.y - point.py) * damping;

    point.px = point.x;  // store current as previous
    point.py = point.y;

    point.x += vx;           // add implied velocity
    point.y += vy + gravity; // add acceleration
}

Why Verlet Is Numerically Stable

Verlet integration is a second-order symplectic method. Its per-step error is proportional to $\Delta t^4$ (versus $\Delta t^2$ for Euler), and it approximately conserves the total energy of a mechanical system over long simulation runs. A Verlet-integrated pendulum will swing at nearly constant amplitude for thousands of frames, while an Euler-integrated pendulum would spiral outward.

The key intuition: by eliminating explicit velocity from the integration loop, Verlet prevents the energy runaway that plagues first-order methods. The implicit velocity is always derived from actual recorded position history, never from accumulated floating-point error.

Velocity Verlet: The Explicit Variant

When your simulation requires an explicit velocity — for velocity-dependent drag, collision response impulses, or friction — the Velocity Verlet variant reintroduces it without sacrificing stability:

$$\vec{x}_{n+1} = \vec{x}_n + \vec{v}_n \Delta t + \frac{1}{2}\vec{a}_n \Delta t^2$$

$$\vec{v}_{n+1} = \vec{v}_n + \frac{\vec{a}_n + \vec{a}_{n+1}}{2} \Delta t$$

This is mathematically equivalent to position Verlet but stores velocity explicitly, making it preferable for fluid simulations, drag forces, and any system where the acceleration at $n+1$ depends on velocity (requiring a prediction-correction loop). For most game cloth and rope physics, the simpler position Verlet is sufficient.

Constraint-Based Physics: Jakobsen's Method

Verlet integration is most effective when combined with iterative constraint relaxation, a technique popularized by Thomas Jakobsen in his 2001 GDC paper Advanced Character Physics. Instead of computing constraint forces analytically (which requires solving systems of equations), simply detect constraint violations after each integration step and directly correct positions to re-satisfy them.

This turns a hard differential equations problem into a simple iterative geometry problem, and it runs fast on the hardware of the early 2000s — and still does today.

Distance Constraints

The most fundamental constraint is the distance constraint: two particles must remain a fixed rest length $L$ apart. After Verlet moves both particles, we measure the actual distance, compute the discrepancy, and push or pull each particle halfway to correct it:

function satisfyDistanceConstraint(a, b, restLength) {
    const dx = b.x - a.x;
    const dy = b.y - a.y;
    const dz = b.z - a.z;
    const dist = Math.sqrt(dx*dx + dy*dy + dz*dz) || 0.0001;

    // How far off are we, as a fraction of current distance?
    const correction = (dist - restLength) / dist;

    // Push each point halfway toward satisfaction (equal mass assumed)
    const halfCx = dx * correction * 0.5;
    const halfCy = dy * correction * 0.5;
    const halfCz = dz * correction * 0.5;

    if (!a.pinned) { a.x += halfCx; a.y += halfCy; a.z += halfCz; }
    if (!b.pinned) { b.x -= halfCx; b.y -= halfCy; b.z -= halfCz; }
}

The correction factor $\frac{d - L}{d}$ scales the delta vector to exactly account for the length discrepancy. Each point moves half the required correction (assuming equal masses; for unequal masses, weight the corrections by the inverse mass ratio).

Iterative Relaxation

A single pass over all constraints rarely produces a satisfactory result — correcting one constraint displaces its shared vertices and can violate adjacent constraints. The solution is to run multiple iterations of constraint satisfaction per frame:

function update(dt) {
    // Step 1: Integrate all free particles forward in time
    for (const particle of particles) {
        if (!particle.pinned) integrateVerlet(particle, GRAVITY, DAMPING);
    }

    // Step 2: Repeatedly project positions onto constraint manifold
    for (let iter = 0; iter < CONSTRAINT_ITERATIONS; iter++) {
        for (const c of constraints) {
            satisfyDistanceConstraint(c.a, c.b, c.restLength);
        }
        // Re-apply boundary conditions every iteration
        for (const p of pinnedParticles) {
            p.x = p.anchorX;
            p.y = p.anchorY;
            p.z = p.anchorZ;
        }
    }
}

More iterations produce more accurate constraint satisfaction but cost more CPU time. For a cloth simulation, 5–15 iterations per frame is typical and produces visually convincing results. Ropes and chains converge faster due to their simpler topology.

Building a Cloth Simulation

Cloth is the canonical demonstration of Verlet + constraints. A cloth is modelled as a 2D grid of mass points connected by three categories of spring constraints:

  • Structural constraints: Horizontal and vertical neighbours at rest length $d$ (the grid spacing). These resist stretching along the cloth's primary axes.
  • Shear constraints: Diagonal neighbours at rest length $d\sqrt{2}$. These prevent the cloth from shearing like a parallelogram — essential for realistic draping.
  • Bend constraints: Skip-one neighbours (two cells apart) at rest length $2d$. These resist sharp folding and give the cloth a sense of stiffness.

Setting up the constraint network:

for (let y = 0; y < ROWS; y++) {
    for (let x = 0; x < COLS; x++) {
        const idx = y * COLS + x;

        // Structural
        if (x < COLS - 1) addConstraint(idx, idx + 1,         SPACING);
        if (y < ROWS - 1) addConstraint(idx, idx + COLS,       SPACING);

        // Shear
        if (x < COLS-1 && y < ROWS-1) {
            addConstraint(idx,   idx + COLS + 1, SPACING * 1.4142);
            addConstraint(idx+1, idx + COLS,     SPACING * 1.4142);
        }

        // Bend
        if (x < COLS - 2) addConstraint(idx, idx + 2,          SPACING * 2);
        if (y < ROWS - 2) addConstraint(idx, idx + COLS * 2,   SPACING * 2);
    }
}

Pin a few points along the top row (by setting them as immovable each frame), and gravity and constraint relaxation do the rest — the cloth drapes naturally and dynamically.

Adding Wind and Forces

External forces are trivially added to this approach. Wind is simply an extra acceleration applied per frame before constraint resolution:

// Oscillating wind, evaluated per frame
const windX = Math.sin(time * 0.8) * WIND_STRENGTH
             + Math.sin(time * 2.1) * WIND_STRENGTH * 0.3;
const windZ = Math.cos(time * 0.5) * WIND_STRENGTH * 0.6;

// Applied during Verlet integration step
newX = currX + (currX - prevX) * DAMPING + windX;
newY = currY + (currY - prevY) * DAMPING + GRAVITY;
newZ = currZ + (currZ - prevZ) * DAMPING + windZ;

Vortex fields, explosion impulses, and character-driven forces are all just additional terms in this equation. No force calculation infrastructure is needed beyond adding numbers to the position update.

Real-World Use in Games and Engines

Verlet integration and Jakobsen constraint physics appear in many shipped games:

  • Hitman (IO Interactive, 2000–present): Jakobsen worked at IO Interactive and his paper described techniques directly used in Hitman's character cloth. NPC ties, scarves, and jacket flaps all use Verlet-based simulation.
  • Half-Life 2 / Source Engine (Valve, 2004): The Source engine's ragdoll physics are built on constraint-based position correction heavily influenced by Jakobsen's method, delivering the ragdoll physics that shipped in 2004.
  • Spider-Man games (Insomniac): Web-swinging mechanics rely on Verlet rope simulations — a chain of constrained point masses that authentically model web elasticity and swing dynamics.
  • Just Cause series (Avalanche Studios): The extensive tether, grapple, and rope mechanics are built on Verlet constraint networks connecting vehicles, structures, and characters.
  • Diablo II and III (Blizzard): Character capes and cloaks used constraint-based cloth physics for reactive secondary animation.
  • The Witcher series (CD Projekt Red): Geralt's coat, horse mane, and environmental cloth elements use Verlet-based simulation for physical responsiveness.

Comparing Numerical Integrators

To understand where Verlet sits in the broader landscape of numerical integration methods:

  • Forward Euler (1st order): $O(\Delta t)$ error, simple, unstable for stiff systems. Energy grows over time.
  • Symplectic Euler (1st order): Updates velocity before position; energy-stable, good alternative when explicit velocity is needed.
  • Position Verlet (2nd order): $O(\Delta t^2)$ error, excellent long-term energy conservation, no explicit velocity storage, naturally compatible with position-based constraints.
  • Runge-Kutta 4 (4th order): $O(\Delta t^4)$ error, very accurate, requires four force evaluations per step. Overkill for most game physics; better suited to spacecraft trajectory simulation.

For game physics, position Verlet's position-based formulation provides an excellent balance: far more stable than Euler, much simpler than RK4, and uniquely compatible with direct position correction constraints.

From Verlet to Position-Based Dynamics

Verlet integration combined with Jakobsen constraint relaxation is the direct precursor to modern Position-Based Dynamics (PBD), formalized by Müller et al. in their 2006 SIGGRAPH paper. PBD generalizes the constraint satisfaction approach to handle collision constraints, volume preservation, stretch limits, and aerodynamic forces — all by directly manipulating particle positions rather than integrating forces.

Modern game engines have embraced PBD for high-performance simulation: Unreal Engine 5's Chaos Cloth, Unity's DOTS cloth solver, and NVIDIA's PhysX all use PBD descendants internally. Understanding position Verlet integration is the essential conceptual foundation for all of these more advanced systems — the same three-line integration loop and the same constraint correction pattern appear at their core.

Practical Implementation Tips

  • Use a fixed timestep. Verlet integration is sensitive to variable $\Delta t$ because implied velocity is proportional to $\vec{x}_n - \vec{x}_{n-1}$, which assumes both steps had the same duration. If your frame rate varies, decouple the physics update into fixed 16ms or 8ms steps with a remainder accumulator.
  • Timestep correction for variable $\Delta t$. If you cannot use fixed steps, apply the Störmer correction: multiply the implied velocity by $\frac{\Delta t_n}{\Delta t_{n-1}}$ before adding acceleration. This compensates for differing step durations.
  • Randomize constraint order. Running constraints in the same order every frame can produce directional bias artifacts (cloth that seems stiffer in one direction). Shuffling the constraint list periodically eliminates this at negligible cost.
  • Sleeping. Points with negligible implied velocity ($|\vec{x}_n - \vec{x}_{n-1}| < \varepsilon$) can skip integration entirely, dramatically reducing CPU load for large static cloth regions.
  • Soft constraints. Multiplying the correction by a factor $k < 1.0$ makes a constraint spring-like rather than rigid — useful for stretchy fabric or elastic bungee ropes. With PBD, this is typically expressed as a compliance parameter.
  • GPU parallelism. Constraints on independent vertex pairs can be solved in parallel. Coloring the constraint graph (like a graph vertex-coloring problem) into non-overlapping sets enables full GPU parallelization — the approach used by modern engine cloth solvers to simulate hundreds of high-resolution cloth characters simultaneously.

Verlet integration is one of those techniques that combines mathematical elegance with practical robustness. Its simplicity — a three-line integration loop and a handful of constraint corrections — belies its expressive power. From the billowing banner above a medieval RPG inn to the precisely tuned web-swinging of a superhero game, this 1967 method from molecular dynamics continues to underpin some of gaming's most satisfying and believable physical interactions.

Comments

Like this article? Consider supporting us

Your support helps us keep creating free game dev content, tutorials, and tools.

Free

$0 /month

Newsletter and public posts

  • Newsletter access
  • Public posts & updates
  • Community access

Studio Backer

$25 /month

Direct impact on development with your name in the credits

  • Everything in Supporter
  • Your name in game credits
  • Priority feature requests
  • Direct developer access
  • Monthly asset downloads