shader_legends_2 · IQVoronoi
IQ Voronoi (cellular noise) — F1 / F2 distance field
Inigo Quilez's classical 2D Voronoi / cellular noise: for each integer cell of the plane a single feature point is placed at cell_centre + jitter*(hash-0.5). For each query pixel, the 3x3 ring of neighbour cells is searched and the closest (F1) and second-closest (F2) feature points are recorded. F2 - F1 highlights Voronoi cell boundaries as crisp luminance lines, and the cell id of the winning point provides a per-cell hash that drives a Quilez cosine palette. The result is the canonical procedural-noise tile that has become a staple of modern shader writing.
Original: https://iquilezles.org/articles/voronoilines/
▶ 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
For each query pixel p: snap to integer (ix, iy); for each (dx, dy) in {-1, 0, 1}^2 the neighbour cell's feature point is offset(ix + dx, iy + dy, jitter*hash). Two smallest Euclidean distances F1, F2 are kept. edge = clip(F2 - F1, 0, 1), cell_col = a + b*cos(2pi*(c*(hue(cell_id) + palette_offset) + d)). Optional in-cell rotation by t*rotate_speed animates the field.
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 360 | 120–720 | Square output size in pixels. |
cells | float | 8 | 2–32 | Number of cells across the [-1, 1] canvas (in cell units). |
jitter | float | 0.9 | 0–1.5 | Per-cell feature-point randomness (0 = grid, 1 = uniform). |
edge_softness | float | 6 | 1–30 | Exponential falloff of the F2-F1 edge mask. |
edge_gain | float | 1.8 | 0.5–6 | Linear gain on (F2-F1) before the falloff. |
rotate_speed | float | 0.05 | -0.3–0.3 | Per-second rotation of the per-cell offset. |
palette_offset | float | 0.5 | 0–1 | Phase offset for the IQ cosine palette. |
time_speed | float | 0 | -0.4–0.4 | Drift speed of the rotate offset per step. |
Reference
First 60 lines of reference.py. Full source:
library_external/shader_legends_2/IQVoronoi/reference.py
from __future__ import annotations
from typing import ClassVar
import numpy as np
def _hash12(x: np.ndarray, y: np.ndarray) -> np.ndarray:
"""1-component hash on integer (x, y) → [0, 1). sin-based."""
p = 127.1 * x + 311.7 * y
return (np.sin(p) * 43758.5453123 - np.floor(np.sin(p) * 43758.5453123)).astype(np.float32)
def _hash22(x: np.ndarray, y: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
"""2-component hash returning two [0,1) streams from one cell id."""
a = _hash12(x, y)
b = _hash12(x + 17.0, y + 31.0)
return a, b
def _iq_voronoi(
xv: np.ndarray,
yv: np.ndarray,
jitter: float,
seed: int,
rotate: float,
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
"""Inigo Quilez's classic 2D Voronoi (cellular) noise.
For each integer cell (ix, iy) we place one feature point at
cell_centre + jitter*(hash-0.5). For each query pixel we search
the 3x3 ring of neighbour cells and keep the *two* smallest
distances (F1, F2). We also return the *cell id* of the winning
point so callers can colourise per cell.
"""
ix = np.floor(xv).astype(np.int64)
iy = np.floor(yv).astype(np.int64)
fx = (xv - ix).astype(np.float32)
fy = (yv - iy).astype(np.float32)
# Per-cell feature points are pre-hashed once so we don't pay a
# sin() per neighbour. We store them in arrays of shape
# (ny_cells, nx_cells, 3 cells * 3 cells * 1 point each = 1).
sx = int(seed * 17) & 0xFFFFF
sy = int(seed * 31) & 0xFFFFF
# Build cell index grids in the larger (3x3) search window
best_d1 = np.full(xv.shape, np.inf, dtype=np.float32)
best_d2 = np.full(xv.shape, np.inf, dtype=np.float32)
best_id_x = np.zeros(xv.shape, dtype=np.int32)
best_id_y = np.zeros(xv.shape, dtype=np.int32)
cs = float(np.cos(rotate))
sn = float(np.sin(rotate))
# Pre-compute feature points relative to cell centres for the
# surrounding cells we touch (3x3 ring). We compute them inside
# the loop using cheap integer hash, no need to materialise a
# grid: 9 cells × 9 hash calls total — numpy is plenty fast. Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/shader_legends_2/IQVoronoi/reference.py Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.
More from shader legends II
Hex Grid Tiling — axial-coord tessellation with fBM-filled cells
shader_legends_2 · HexGridTiling
A hexagonal grid is the canonical 6-fold-symmetric alternative to the square-grid hash tiling.
IQ Domain Warping — fbm composed with itself at 2/3 levels
shader_legends_2 · IQDomainWarping
Inigo Quilez's domain warping trick: feed a noise function *itself* as input coordinates, producing organic flowing patterns that no plain noise function can match.
IQ Mandelbox (2D edition) — iterated box-fold / ball-fold / scale / translate
shader_legends_2 · IQMandelbox2D
The Mandelbox is a 3D fractal defined by the iterate v := scale * box_fold(ball_fold(v)) + c, parametrised by a 'scale' fold factor and the (r_min, r_fixed) ball-fold thresholds.
Lorenz Attractor (3D ODE) — strange-attractor trajectory with depth shading
shader_legends_2 · LorenzAttractor3D
The Lorenz system (1963) is the canonical strange-attractor: x' = sigma(y - x); y' = x(rho - z) - y; z' = xy - beta*z.
Menger Sponge, 2D Carpet edition — recursive 3x3 / centre-removal
shader_legends_2 · MengerSponge2D
The Menger sponge (1926) is a 3D fractal formed by sub-dividing each cube into 3x3x3 and removing the centre plus the six face-centres.