Skip to content
Sample of IQ Voronoi (cellular noise) — F1 / F2 distance field

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.

Family
shader_legends_2
Source
shader legends II
Evolution
cellular
Render
scalar_field
Complexity
medium
Agents
0
Year
2012
Author
Inigo Quilez

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

Sample of Hex Grid Tiling — axial-coord tessellation with fBM-filled cells

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.

View →
Sample of IQ Domain Warping — fbm composed with itself at 2/3 levels

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.

View →
Sample of IQ Mandelbox (2D edition) — iterated box-fold / ball-fold / scale / translate

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.

View →
Sample of Lorenz Attractor (3D ODE) — strange-attractor trajectory with depth shading

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.

View →
Sample of Menger Sponge, 2D Carpet edition — recursive 3x3 / centre-removal

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.

View →

See all shader_legends_2 artworks →