toxi_openframeworks · ParticleSpring2D
Verlet Particle-Spring Network
A Verlet-integrated particle network connected to its k-nearest neighbours by Hooke's-law springs. Four corner particles are pinned (top-left, top-right, bottom-left, bottom-right), giving the mesh a tangible, hammock-like behaviour. Per step the system Verlet-integrates (x_new = 2x - x_prev + a dt^2) and then runs N rounds of Jakobsen constraint relaxation to drive each spring back toward its rest length. This is the same integrator behind `ofxMSAPhysics`, `ofxBox2d`, and `toxiclibs.physics`.
Original: https://github.com/memo/ofxMSAPhysics
▶ 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: x_{t+1} = 2*x_t - x_{t-1} + a*dt^2. Constraint: for each edge (i, j), diff = p_j - p_i, d = |diff|, delta = rest - d, offset = 0.5 * delta * spring_k * diff/d. Move p_i by +offset, p_j by -offset unless one endpoint is anchored. Repeat N rounds per step.
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 500 | 200–1200 | Square canvas side length in pixels. |
num_particles | int | 80 | 20–400 | Total number of free + anchored particles. |
spacing | float | 40 | 8–120 | Target rest length (px) for newly-initialised springs. |
spring_k | float | 0.05 | 0.005–0.5 | Stiffness factor applied to constraint relaxation (0 = no pull, 1 = snap). |
damping | float | 0.985 | 0.7–0.999 | Per-step velocity damping. |
gravity | float | 60 | 0–300 | Vertical acceleration applied each step. |
constraint_iterations | int | 6 | 1–20 | Rounds of Jakobsen relaxation per step. |
anchor_corners | bool | true | — | Pin the four image corners. |
anchor_bottom | bool | false | — | Pin a row of particles along the bottom edge. |
anchor_top | bool | false | — | Pin a row of particles along the top edge. |
trail_decay | float | 1 | 0.7–1 | Per-render multiplicative fade of the canvas. |
node_radius | int | 2 | 0–5 | Radius (px) of the node dots. |
line_alpha | float | 0.85 | 0.1–1 | Blend factor for new strokes. |
seed | int | null | 0–999999 | RNG seed. |
Reference
First 60 lines of reference.py. Full source:
library_external/toxi_openframeworks/ParticleSpring2D/reference.py
from __future__ import annotations
from typing import ClassVar
import numpy as np
class ParticleSpring2D:
"""Verlet-integrated particle-spring network with 4 corner anchors.
A set of free particles is connected to its k-nearest neighbours
by Hooke's-law springs of rest length ``spacing``. Four corner
particles are pinned (anchored) so the network behaves like a
deformable mesh. Per step:
1. Verlet integrate (x_new = 2*x - x_prev + a*dt^2)
2. N rounds of constraint relaxation (Jakobsen 2001)
This is the canonical mass-spring formulation used in the oF
community — `ofxMSAPhysics` (Memo Akten), `ofxBox2d`, and
Karsten Schmidt's `toxiclibs.physics` all rely on it. The version
here is a pure-numpy port of `toxiclibs.physics.VerletPhysics`
with explicit corners.
"""
family: ClassVar[str] = "toxi_openframeworks"
sketch: ClassVar[str] = "particlespring2d"
def __init__(
self,
dim: int = 500,
num_particles: int = 80,
spacing: float = 40.0,
spring_k: float = 0.05,
damping: float = 0.985,
gravity: float = 60.0,
constraint_iterations: int = 6,
anchor_corners: bool = True,
anchor_bottom: bool = False,
anchor_top: bool = False,
trail_decay: float = 1.0,
node_radius: int = 2,
line_alpha: float = 0.85,
bg_color: tuple[int, int, int] = (245, 240, 230),
link_color: tuple[int, int, int] = (60, 70, 90),
node_color: tuple[int, int, int] = (200, 70, 60),
seed: int | None = None,
) -> None:
self.dim = dim
self.num_particles = num_particles
self.spacing = spacing
self.spring_k = spring_k
self.damping = damping
self.gravity = gravity
self.constraint_iterations = constraint_iterations
self._anchor_corners = anchor_corners
self._anchor_bottom = anchor_bottom
self._anchor_top = anchor_top
self.trail_decay = trail_decay
self.node_radius = node_radius Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/toxi_openframeworks/ParticleSpring2D/reference.py Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.
More from toxi · openFrameworks
Generative Type Letterforms
toxi_openframeworks · GenerativeTypeLetterforms
Letterforms assembled from a 9×9 stroke grid alphabet (A–Z subset), then modulated by a parametric function (bar widths and stroke positions follow `a * sin(b*x + c)` style curves) and optionally recursively subdivided into smaller marks.
Gray-Scott Reaction-Diffusion
toxi_openframeworks · ReactionDiffusionGS
The canonical two-species Gray-Scott reaction-diffusion model on a periodic square grid: dU/dt = Du*lap(U) - U*V^2 + F*(1-U), dV/dt = Dv*lap(V) + U*V^2 - (F+k)*V.
Median-Cut Color Quantizer
toxi_openframeworks · ColorQuantizer
Heckbert's 1982 median-cut color quantization applied to an evolving procedural gradient + low-resolution noise wash.
Pixel Sort (Glitch Art)
toxi_openframeworks · PixelSort
Per-row pixel sorting à la Kim Asendorf's `ASDFPixelSort` and Karsten Schmidt's own glitch-art Processing sketches.
Sound-Reactive Particle Field (Synthetic Audio)
toxi_openframeworks · SoundReactiveField
Particles advected by a force field whose horizontal component is driven by a synthetic audio signal: a chain of N sinusoids at different frequencies/amplitudes (the 'audio'), with the audio's instantaneous value plus its time derivative ('beat onset') driving the field.
Voronoi Tessellation with Lloyd Relaxation
toxi_openframeworks · VoronoiRelaxation
Lloyd's algorithm applied to a jittered point cloud: each seed is replaced with the centroid of its Voronoi cell, repeatedly, producing ever-more-uniform polygonal cells.