Skip to content
Sample of 3D Menger sponge level-3 wireframe

fractals_3d_webgl · MengerSponge3DLevel3

3D Menger sponge level-3 wireframe

A level-3 3D Menger sponge explicitly generated as 20^3 = 8000 kept sub-cubes and rasterised as a 12-edge wireframe projection. The construction recursively replaces each cube with 20 of the 27 sub-cubes in a 3x3x3 partition, keeping only the eight corners (the centre, six face-centres, and twelve edge-centres are removed at each step). Hausdorff dimension is log(20)/log(3) ~= 2.727. The full 3D cube cloud is sorted back-to-front, then orthographically projected from a slow camera orbit into a 2D canvas, with depth fading the wire colour from near-white to cool blue at the far side.

Original: https://en.wikipedia.org/wiki/Menger_sponge

▶ 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
fractals_3d_webgl
Source
fractals_3d_webgl
Evolution
fractal
Render
wireframe
Complexity
high
Agents
8000
Year
1926
Author
Karl Menger (1926)

Math essence

Per level, divide each cube into 3x3x3 = 27 equal sub-cubes; keep sub-cube (i, j, k) iff at most one of (i, j, k) is 1. This leaves 8 corner sub-cubes. After N levels there are 20^N kept cubes, each of side length 2/3^N. Here N = 3, so 8000 cubes. Project each cube centre by a 3D pitch/yaw rotation, sort by projected depth, rasterise each cube as 12 line segments using np.linspace on (x, y) endpoints, and blend the wire colour by (1 - depth) to dim the far side.

Parameters

Name Type Default Range Description
dim int 256 128–512 Square canvas side length in pixels.
level int 3 1–3 Recursion depth (level N has 20^N sub-cubes; level 3 = 8000).
camera_pitch float 0.85 -1.2–1.2 Camera pitch (radians), animated by step().
camera_yaw float 0.6 -1.2–1.2 Camera yaw (radians), animated by step().
orbit_speed float 0.1 0–0.4 Per-step camera phase increment.
wire_color tuple 245,250,255 — Wire colour at the near side (low depth).
far_color tuple 110,140,220 — Wire colour at the far side (high depth).
background tuple 8,10,20 — Background colour.
depth_fade float 0.85 0–1 Depth-based wire alpha multiplier.
seed int 7 0–999 RNG seed.

Reference

First 60 lines of reference.py. Full source: library_external/fractals_3d_webgl/MengerSponge3DLevel3/reference.py

from __future__ import annotations

from typing import ClassVar

import numpy as np


def _is_menger_kept(i: int, j: int, k: int) -> bool:
    """Return True if the (i, j, k) sub-cube (each in {0, 1, 2}) of a
    3x3x3 macro-cell is *kept* in the Menger sponge construction.

    Per Wikipedia (Menger sponge): keep the cube iff at most one of
    (i, j, k) is 1.  Equivalently, drop the centre, the six
    face-centres, and the twelve edge-centres; keep the eight corners.
    """
    ones = (i == 1) + (j == 1) + (k == 1)
    return ones <= 1


def _generate_menger_cubes(
    level: int, kept: bool = True
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """Recursively generate the kept (``kept=True``) or removed
    (``kept=False``) sub-cubes of a level-``level`` Menger sponge
    centred at the origin with side length 2.

    Returns (xs, ys, zs, sizes) for every kept cube, where each array
    has shape (N,).  Coordinates are cube centres; ``sizes`` is the
    side length of each cube.  N is 20^level (kept) or 7^level
    (removed).
    """
    cubes: list[tuple[float, float, float, float]] = [(0.0, 0.0, 0.0, 2.0)]
    for _ in range(level):
        new: list[tuple[float, float, float, float]] = []
        for cx, cy, cz, side in cubes:
            child = side / 3.0
            for i in range(3):
                for j in range(3):
                    for k in range(3):
                        is_kept = _is_menger_kept(i, j, k)
                        if (kept and is_kept) or (not kept and not is_kept):
                            ox = (i - 1) * child
                            oy = (j - 1) * child
                            oz = (k - 1) * child
                            new.append((cx + ox, cy + oy, cz + oz, child))
        cubes = new
    arr = np.array(cubes, dtype=np.float32)
    return arr[:, 0], arr[:, 1], arr[:, 2], arr[:, 3]


def _project_cube_wireframe(
    px: np.ndarray,
    py: np.ndarray,
    d: int,
    scale: float,
    ox: float,
    oy: float,
    color: np.ndarray,
    frame: np.ndarray,
    line_alpha: float = 0.85,

Try running it locally

The reference implementation is self-contained Python with numpy. Install the package and run:

python library_external/fractals_3d_webgl/MengerSponge3DLevel3/reference.py

Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.

More from fractals_3d_webgl

See all fractals_3d_webgl artworks →