shader_legends_2 · MengerSponge2D
Menger Sponge, 2D Carpet edition — recursive 3x3 / centre-removal
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. The 2D version (Menger carpet) is the shader community's standard recursive-fractal primitive, used in BigWings's iconic Mandelbox/Sponge shaders. Each level divides each surviving cell into 3x3 and removes only the centre, leaving 8/9 per cell with self-similar boundary structure. This formula renders the per-pixel 'kill level' (which iteration first cut the pixel), giving the carpet a depth-cued hue gradient.
Original: https://www.shadertoy.com/view/Mdf3zX
▶ 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
Iterate levels times: subdivide p into 3x3 sub-cells; mark (sub_x == 1 && sub_y == 1) as killed at this level. Survivors (kill == levels) are kept; killed pixels are recorded with the level that cut them. The render maps each kill level to a different hue band via level_hue[level] * hue_shift, modulated by an IQ cosine palette. A per-cell hash tints survivors; the sub-square boundary distance is multiplied by edge_softness to mark outlines.
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 360 | 120–720 | Square output size in pixels. |
levels | int | 4 | 1–6 | Number of subdivision iterations. 4 -> 81x81 cells; 5 -> 243x243. |
edge_softness | float | 2 | 0.5–20 | Brightness multiplier for the in-sub-square edge falloff. |
hue_shift | float | 0.18 | 0–1 | Hue spacing between adjacent kill levels. |
palette_offset | float | 0.5 | 0–1 | Base phase offset for the IQ cosine palette. |
contrast | float | 1 | 0.4–3 | Post-render contrast multiplier. |
time_speed | float | 0.04 | -0.3–0.3 | Drift speed of the palette phase per step. |
Reference
First 60 lines of reference.py. Full source:
library_external/shader_legends_2/MengerSponge2D/reference.py
from __future__ import annotations
from typing import ClassVar
import numpy as np
def _hash11(x: np.ndarray) -> np.ndarray:
p = 127.1 * x
return (np.sin(p) * 43758.5453123 - np.floor(np.sin(p) * 43758.5453123)).astype(np.float32)
def _hash12(x: np.ndarray, y: np.ndarray) -> np.ndarray:
p = 127.1 * x + 311.7 * y
return (np.sin(p) * 43758.5453123 - np.floor(np.sin(p) * 43758.5453123)).astype(np.float32)
def _depth_levels(
xv: np.ndarray,
yv: np.ndarray,
levels: int,
) -> np.ndarray:
"""Per-pixel *kill depth* of the 2D Menger sponge (carpet).
Returns the integer level at which the pixel gets cut. Pixels
outside any cut are capped at `levels` (survivors).
"""
kill = np.full(xv.shape, levels, dtype=np.int32)
size = 1.0
cx = 0.0
cy = 0.0
s = 1.0
for lev in range(levels):
x = xv * s + cx
y = yv * s + cy
ix = np.floor(x / size).astype(np.int64)
iy = np.floor(y / size).astype(np.int64)
fx = (x / size) - ix.astype(np.float32)
fy = (y / size) - iy.astype(np.float32)
sub_x = np.floor(fx * 3.0).astype(np.int64)
sub_y = np.floor(fy * 3.0).astype(np.int64)
centre_hit = (sub_x == 1) & (sub_y == 1)
# Update: pixels that get hit at this level and haven't
# been assigned an earlier kill level get lev.
first_hit = centre_hit & (kill == levels)
kill = np.where(first_hit, lev, kill)
s *= 0.3333333
cx = cx * 0.3333333
cy = cy * 0.3333333
return kill
def _sponge_mask(
xv: np.ndarray,
yv: np.ndarray,
levels: int,
) -> np.ndarray:
"""Boolean survival mask for the Menger carpet: True => pixel
is *in* the fractal (survives all levels)."""
kill = _depth_levels(xv, yv, levels) Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/shader_legends_2/MengerSponge2D/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.
IQ Voronoi (cellular noise) — F1 / F2 distance field
shader_legends_2 · IQVoronoi
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).
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.