toxi_openframeworks · VoronoiRelaxation
Voronoi Tessellation with Lloyd Relaxation
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. To stay interactive on pure numpy, the nearest-seed partition for centroid estimation is computed on a `centroid_sub` × `centroid_sub` subsample; the final image is rendered at full resolution. Each step also adds a small Gaussian jitter to every seed so the cells drift apart and reform.
Original: https://codeberg.org/thi.ng/umbrella/src/branch/develop/packages/geom-voronoi
▶ 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
Lloyd step: for each pixel (x, y) in a coarse grid, compute argmin_i |(x, y) - p_i|^2; for each seed, average the positions of all pixels assigned to it; blend old and new position by `relaxation_blend`. Step adds Gaussian noise to seeds before each Lloyd pass, so cells slowly drift. Render is the nearest-seed partition at full resolution, colored by the seed's palette entry.
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 320 | 120–1000 | Square canvas side length in pixels. |
num_seeds | int | 64 | 4–400 | Number of Voronoi seeds. |
lloyd_steps | int | 6 | 0–60 | Lloyd relaxation rounds run in reset() before the first render. |
step_jitter | float | 4 | 0–60 | Per-step Gaussian jitter (σ px) added to each seed. |
relaxation_blend | float | 0.65 | 0.05–1 | Lloyd step blend (1.0 = full snap to centroid; 0 = no movement). |
centroid_sub | int | 48 | 16–96 | Subsample resolution for centroid estimation (lower = faster but coarser). |
palette_mode | str | earth | — | Palette of cell colors. |
show_seeds | bool | true | — | Overlay small dots on each seed after rendering. |
seed_radius | int | 2 | 0–6 | Radius (px) of the seed overlay dots. |
seed | int | null | 0–999999 | RNG seed. |
Reference
First 60 lines of reference.py. Full source:
library_external/toxi_openframeworks/VoronoiRelaxation/reference.py
from __future__ import annotations
from typing import ClassVar
import numpy as np
class VoronoiRelaxation:
"""Voronoi tessellation with Lloyd relaxation (toxi / thi.ng style).
Lloyd's algorithm iteratively replaces each seed with the centroid
of its Voronoi cell, producing ever-more-uniform polygons. The
same algorithm is the basis of Karsten Schmidt's oF Voronoi
tessellation patches (early-2010s demos) and the
`@thi.ng/geom-voronoi` relaxation examples. To stay interactive on
pure numpy, the nearest-seed partition is computed on a
``sub`` × ``sub`` subsample for centroid estimation and on a
full-resolution grid for the final image.
"""
family: ClassVar[str] = "toxi_openframeworks"
sketch: ClassVar[str] = "voronoirelaxation"
def __init__(
self,
dim: int = 320,
num_seeds: int = 64,
lloyd_steps: int = 6,
step_jitter: float = 4.0,
relaxation_blend: float = 0.65,
centroid_sub: int = 48,
palette_mode: str = "earth",
show_seeds: bool = True,
seed_radius: int = 2,
bg_color: tuple[int, int, int] = (245, 240, 230),
seed_color: tuple[int, int, int] = (35, 35, 50),
seed: int | None = None,
) -> None:
self.dim = dim
self.num_seeds = num_seeds
self.lloyd_steps = lloyd_steps
self.step_jitter = step_jitter
self.relaxation_blend = relaxation_blend
self.centroid_sub = centroid_sub
self._palette_mode = palette_mode
self.show_seeds = show_seeds
self.seed_radius = seed_radius
self.bg_color = np.array(bg_color, dtype=np.float32)
self.seed_color = np.array(seed_color, dtype=np.float32)
self._seed = seed
self.reset()
def reset(self, seed: int | None = None) -> None:
if seed is not None:
self._seed = seed
self.rng = np.random.default_rng(self._seed)
self.pts = self.rng.uniform(0, self.dim, size=(self.num_seeds, 2))
self.colors = self._palette(self.num_seeds)
for _ in range(self.lloyd_steps):
self.pts = self._lloyd(self.pts, sub=self.centroid_sub) Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/toxi_openframeworks/VoronoiRelaxation/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.
Verlet Particle-Spring Network
toxi_openframeworks · ParticleSpring2D
A Verlet-integrated particle network connected to its k-nearest neighbours by Hooke's-law springs.