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.
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
Additive Waves — 3D Terrain
toxi_toxiclibs · AdditiveWaves3D
A 2D height-field terrain driven by the sum of multiple wave generators (sine, FBM noise, sawtooth, constant offset) defined in Karsten Schmidt's toxiclibscore wave module.
Super-Ellipsoid Mesh
toxi_toxiclibs · SuperEllipsoid
A super-ellipsoid is a 3D primitive that smoothly interpolates between a sphere, a box, and a pinched pillow via two shape parameters e1 (longitude exponent) and e2 (latitude exponent).
Winged-Edge Mesh
toxi_toxiclibs · WingedEdgeMesh
A half-edge / winged-edge data structure for triangle meshes, designed for sub-million-triangle scenes where simple flat vertex/index arrays become cumbersome.