Skip to content
Sample of Verlet Physics 3D — Mass-Spring Cloth

toxi_toxiclibs · VerletPhysics3D

Verlet Physics 3D — Mass-Spring Cloth

A 3D mass-spring system integrated with Verlet position-based dynamics and Jakobsen-style constraint relaxation. Particles carry position, previous position, mass, and a 'locked' flag. Springs are length-preserving (distance) constraints between pairs of particles; bending constraints span three-particle chains; spherical and box constraints keep particles inside (or outside) a region. Each step: integrate (x' = x + (x - x_prev) * damping + a * dt²), then relax constraints 8-16 iterations. Karsten's toxiclibs implementation is the reference Java port of the algorithm and underpins the cloth simulations, soft-body demos, and 'wobbly jelly' toys used across the openFrameworks and Processing communities. The 3D version is the natural extension of the 2D version (toxi_openframeworks/ParticleSpring2D) and demonstrates how the same Verlet abstraction generalizes to spatial simulations.

Original: https://github.com/postspectacular/toxiclibs/tree/master/src.core/toxi/physics/verlet

▶ Try the live demo (JS port) ▶ Run reference.py via Pyodide 10 formulas have instant JS ports. The Pyodide path runs the actual Python in your browser — first load is ~6 MB, then instant.

Family
toxi_toxiclibs
Source
toxi_toxiclibs
Evolution
physics
Render
spring_3d_mesh
Complexity
medium
Agents
196
Year
2008
Author
Karsten Schmidt (toxiclibs.physics, 2008) / Thomas Jakobsen (Verlet integration, 2001)

Math essence

Verlet integration: x_new = 2·x - x_prev + a·dt². Damping: x_new = x + (1-d)·(x - x_prev) + a·dt². Constraint relaxation: for each spring with rest length L and stiffness k, move each end by ±(k/2)·(current_length - L)·normalized_direction. Bending constraints: a chain (a, b, c) gets rest_angle preservation via a perpendicular-offset projection. NaN/Infinity guard on every step. Floor plane: y >= 0 with reflection (v_new = -v_old * restitution).

Parameters

Name Type Default Range Description
n_grid int 14 4–32 Cloth grid side (n×n particles).
size float 4 1–10 Cloth side length in world units.
gravity float 0.3 0–2 Y-axis gravity acceleration per step.
damping float 0.98 0.5–1 Verlet velocity damping (1 = none).
iterations int 12 1–32 Constraint-relaxation iterations per step.
stiffness float 1 0–1 Spring stiffness (clamped 0..1 in the Jakobsen style).
wind float 0 -1–1 Constant wind in +X direction.
seed int null 0–999999 RNG seed for initial perturbation.

Reference

First 60 lines of reference.py. Full source: library_external/toxi_toxiclibs/VerletPhysics3D/reference.py

"""VerletPhysics3D — 3D mass-spring cloth simulation (Jakobsen 2001 style).

A pure-numpy port of Karsten Schmidt's toxiclibs.physics.VerletPhysics3D
(Karsten Schmidt, 2008). The algorithm uses position-based dynamics:

  * Each particle stores (x, x_prev) instead of (x, v).
  * Integration: x_new = x + (x - x_prev) * damping + a * dt²
  * Constraint relaxation: for each spring, project both ends toward
    the rest length by ±(k/2) * (current - rest) * direction.

Bending constraints span 3-particle chains and preserve the rest
angle; the floor is a hard plane (y >= 0) with restitution.

The 2D version is `toxi_openframeworks/ParticleSpring2D`. This is the
3D extension.
"""
from __future__ import annotations

import numpy as np


class VerletPhysics3D:
    """3D Verlet mass-spring system with cloth-grid initializer."""

    def __init__(self, n_grid=14, size=4.0, gravity=0.3, damping=0.98,
                 iterations=12, stiffness=1.0, wind=0.0, seed=0):
        self.n = n_grid
        self.size = size
        self.gravity = gravity
        self.damping = damping
        self.iterations = iterations
        self.stiffness = stiffness
        self.wind = wind
        self.rng = np.random.default_rng(seed)

        # Build a square cloth grid in the XZ plane at y = size/2.
        spacing = size / n_grid
        half = size / 2
        coords = np.array(
            [[(i - n_grid/2) * spacing, half, (j - n_grid/2) * spacing]
             for j in range(n_grid) for i in range(n_grid)],
            dtype=np.float32,
        )
        n_particles = n_grid * n_grid
        self.n_particles = n_particles
        self.pos = coords.copy()
        self.pos_prev = coords.copy()
        # Pin the four corners (so the cloth hangs)
        self.locked = np.zeros(n_particles, dtype=bool)
        for (i, j) in [(0, 0), (n_grid-1, 0), (0, n_grid-1), (n_grid-1, n_grid-1)]:
            self.locked[j * n_grid + i] = True
        # Slight initial perturbation so the relaxation has something to do
        self.pos += self.rng.normal(0, 0.01, size=self.pos.shape).astype(np.float32)

        # Build springs: structural (4-neighbors) + shear (diagonals)
        # + bending (skip-one neighbors). Each is a (a, b) pair.
        springs = []
        for j in range(n_grid):
            for i in range(n_grid):
                idx = j * n_grid + i

Try running it locally

The reference implementation is self-contained Python with numpy. Install the package and run:

python library_external/toxi_toxiclibs/VerletPhysics3D/reference.py

Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.

More from toxi_toxiclibs

See all toxi_toxiclibs artworks →