shader_legends · IQDomainRepetition
Domain Repetition — Inigo Quilez tile-with-warp
Inigo Quilez's 'Domain Repetition' technique from his 2008 article. A base SDF (rounded box / cross) is replicated infinitely over space by wrapping the sample point with `r = p - s * round(p / s)`; to keep the SDF correct for shapes that break tile-boundary symmetry (rotation, scale, jitter) we evaluate a 2x2 ring of neighbour tiles and keep the min distance. Per-instance rotation angle, scale, and colour come from a 2D hash of the tile id. We render the resulting SDF as a heightmap tinted with Quilez's cosine palette and gently warped in time.
Original: https://iquilezles.org/articles/sdfrepetition/
▶ 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 a query point p, take id = round(p/s). To guarantee correct distances even with size/rotation variation, scan the 2x2 ring id + di*sign(p - s*id), evaluate sdf(rx, ry) per neighbour after a per-instance rotation/scale, and keep the minimum. Inside each tile, the SDF is a rounded box minus a cross; outside any tile the SDF is positive. The cosine palette `a + b*cos(2π*(c*h + d))` colours each tile by hash(id).
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 360 | 120–800 | Square output size in pixels. |
scale | float | 6 | 2–24 | Tile size in canvas units. |
smoothness | float | 0.5 | 0–2 | Smoothing amount for the SDF edge (visual). |
warp | float | 0.35 | 0–2 | Strength of the fBM-driven domain warp. |
palette_offset | float | 0.5 | 0–1 | Phase offset for the cosine palette. |
time_speed | float | 0.05 | -0.4–0.4 | Drift speed of the domain warp per step. |
Reference
First 60 lines of reference.py. Full source:
library_external/shader_legends/IQDomainRepetition/reference.py
from __future__ import annotations
from typing import ClassVar
import numpy as np
def _hash1(x: np.ndarray, y: np.ndarray) -> np.ndarray:
x = x.astype(np.float64)
y = y.astype(np.float64)
p = 127.1 * x + 311.7 * y
return (np.sin(p) * 43758.5453123 - np.floor(np.sin(p) * 43758.5453123)).astype(np.float32)
def _hash2(x: np.ndarray, y: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
a = _hash1(x, y)
b = _hash1(x + 17.0, y + 31.0)
return a, b
def _value_noise2(x: np.ndarray, y: np.ndarray, seed: int) -> np.ndarray:
xi = np.floor(x).astype(np.int64)
yi = np.floor(y).astype(np.int64)
xf = (x - xi).astype(np.float32)
yf = (y - yi).astype(np.float32)
xi_i = (xi % (1 << 24)).astype(np.int32)
yi_i = (yi % (1 << 24)).astype(np.int32)
sx = np.float32((seed * 17) % (1 << 20))
sy = np.float32((seed * 31) % (1 << 20))
u = xf * xf * (3.0 - 2.0 * xf)
v = yf * yf * (3.0 - 2.0 * yf)
n00 = _hash1(xi_i.astype(np.float32) + sx, yi_i.astype(np.float32) + sy)
n10 = _hash1(xi_i.astype(np.float32) + np.float32(1) + sx, yi_i.astype(np.float32) + sy)
n01 = _hash1(xi_i.astype(np.float32) + sx, yi_i.astype(np.float32) + np.float32(1) + sy)
n11 = _hash1(xi_i.astype(np.float32) + np.float32(1) + sx, yi_i.astype(np.float32) + np.float32(1) + sy)
nx0 = n00 * (1 - u) + n10 * u
nx1 = n01 * (1 - u) + n11 * u
return nx0 * (1 - v) + nx1 * v
def _fbm(x: np.ndarray, y: np.ndarray, seed: int, octaves: int = 4) -> np.ndarray:
f = 1.0
a = 1.0
total = np.zeros_like(x, dtype=np.float32)
norm = 0.0
for k in range(octaves):
total += a * _value_noise2(x * f + 31.0 * k, y * f - 17.0 * k, seed + k)
norm += a
f *= 2.0
a *= 0.55
return total / max(norm, 1e-6)
def _shape_sdf(rx: np.ndarray, ry: np.ndarray) -> np.ndarray:
"""Base SDF: a rounded rectangle + a small inner triangle blob.
Defined on (-0.5, 0.5) — domain repetition expects shapes to live
within a single tile, with the tile size giving the period.
"""
qx = np.abs(rx) - 0.32 Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/shader_legends/IQDomainRepetition/reference.py Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.
More from shader legends
Hash without Sine — Dave Hoskins hash art
shader_legends · TaterHashArt
Dave Hoskins's celebrated 'Hash without Sine' family of cross-platform-stable pseudo-random hash functions, presented as art.
Improved Noise — Ken Perlin (SIGGRAPH 2002, faithful reimplementation)
shader_legends · KenPerlinNoisePure
Ken Perlin's Improved Noise (SIGGRAPH 2002) ported faithfully to numpy.
Pixel City — top-down procedural city
shader_legends · PolygoncityPixelCity
A top-down 50x50 procedural city built from a deterministic hash of grid cells.
Shane Flow Field — particles in an fBM flow
shader_legends · ShaneFlowFieldFBM
Inspired by Shane's Noisey Image Flow Lines (Shadertoy 7sSSzw).
Shane Organic Noise — layered Perlin with rotation and twist
shader_legends · ShaneOrganicNoise
Shane Olsson's 'organic noise' Shadertoy technique: a base Perlin-style gradient noise is sampled in multiple octaves, with each successive layer rotated by an angle proportional to the layer index and twisted by sinusoidal warping about the canvas centre.