Skip to content
Sample of Verlet Particle-Spring Network

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.

Family
toxi_openframeworks
Source
toxi · openFrameworks
Evolution
particle
Render
linked_lines
Complexity
medium
Agents
80
Year
2008
Author
Memo Akten (ofxMSAPhysics) / Karsten Schmidt (toxiclibs.physics)

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

Sample of Generative Type Letterforms

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.

View →
Sample of Gray-Scott Reaction-Diffusion

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.

View →
Sample of Median-Cut Color Quantizer

Median-Cut Color Quantizer

toxi_openframeworks · ColorQuantizer

Heckbert's 1982 median-cut color quantization applied to an evolving procedural gradient + low-resolution noise wash.

View →
Sample of Pixel Sort (Glitch Art)

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.

View →
Sample of Sound-Reactive Particle Field (Synthetic Audio)

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.

View →
Sample of Voronoi Tessellation with Lloyd Relaxation

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.

View →

See all toxi_openframeworks artworks →