De Bruijn Tilings

Generating aperiodic rhombus tilings from grid intersections — Penrose tilings and beyond.

Wide Penrose tiling hero image
A tiling generated from a de Bruijn grid: python main.py -N 25 --box-width 6 --box-height 3

The de Bruijn method

In 1981, N. G. de Bruijn discovered that Penrose tilings can be constructed with a simple formula:

  1. Draw N families of parallel lines at evenly spaced angles. For a Penrose tiling, N=5 gives families at 36° apart. Each family has lines at integer spacings, slightly offset so that no three lines ever meet at the same point.
  2. At each intersection of two lines, the angle between the two families determines a rhombus. The rhombus edges are unit-length, perpendicular to the two crossing families. With N=5 families, only two shapes appear: a wide rhombus (72°) and a thin rhombus (36°).
  3. Assemble the tiling by placing rhombi edge-to-edge, walking outward from a seed via BFS along shared grid lines. The result is a gap-free, overlap-free aperiodic tiling of the plane.
N=2 tiling N=3 tiling N=4 tiling N=5 tiling N=6 tiling N=7 tiling N=8 tiling N=9 tiling N=10 tiling
N=2 grid N=3 grid N=4 grid N=5 grid N=6 grid N=7 grid N=8 grid N=9 grid N=10 grid
N=2 grid + rhombi N=3 grid + rhombi N=4 grid + rhombi N=5 grid + rhombi N=6 grid + rhombi N=7 grid + rhombi N=8 grid + rhombi N=9 grid + rhombi N=10 grid + rhombi
N=2 tiling N=3 tiling N=4 tiling N=5 tiling N=6 tiling N=7 tiling N=8 tiling N=9 tiling N=10 tiling
Grid lines → rhombi at intersections → assembled tiling.
N = 5

Assembly walkthrough

The key insight is that the grid lines encode all the combinatorial information needed to tile the plane. The assembly step is purely topological — no optimization or backtracking. Here is that process animated step by step:

Tiles are glued one by one along shared grid-line edges, growing outward in breadth-first order.

Why it works: the offset trick

Each line in family j satisfies:

Two lines from different families always intersect at exactly one point. But if three lines meet at the same point, the construction breaks — we'd get an ambiguous vertex. De Bruijn showed this can never happen if the offsets are all rational, distinct, and no two sum to an integer. The proof is elegant: at a triple intersection, certain ratios of cosines would need to be rational, but values produce only irrational ratios (or unity, which distinct offsets exclude).

Generalizations

The method generalizes beyond N=5. Any N≥3 produces a valid tiling with ⌊N/2⌋ distinct rhombus types. Higher N yields increasingly intricate patterns with higher-order rotational symmetry.

Grid types

Beyond the classic regular grid, I messed around with a few other grid constructions:

The four grid types. Each produces a valid rhombus tiling with distinct visual character.

Usage

The code is at github.com/samacqua/tilings.

# Generate a classic Penrose tiling
python main.py -N 5 -B 5 -O out

# Try different grid types
python main.py -N 5 -G random -S 42 -O out/random
python main.py -N 5 -G projection -S 42 -O out/projection

# Higher-order symmetry
python main.py -N 12 -B 4 -O out/n12

Each run produces SVGs of the grid, the rhombi at intersections, and the final assembled tiling. Rhombi are colored by their smallest internal angle, giving each distinct tile shape its own color.

References