Wave Function Collapse — Constraint-Driven Procedural Generation
Wave Function Collapse (WFC) by Maxim Gumin (2016) generates tile-based levels, textures, and 3d voxel worlds that look hand-crafted — by framing procedural generation as a constraint satisfaction problem borrowed from quantum mechanics metaphor. The algorithm "observes" cells one at a time, collapsing their superposition of possible tiles, then propagates the resulting constraints to neighbours via arc consistency. Understanding WFC unlocks a family of AI-driven content generation tools used throughout modern game development.
1. The Superposition Metaphor
Imagine a grid where each cell can be any of T tile types. Initially every cell has all T tiles in its "superposition" (the set of still-possible tiles). The goal is to assign exactly one tile to each cell such that all adjacency constraints are satisfied.
2. Entropy-Guided Observation
Which cell to collapse next? Always pick the cell with the lowest Shannon entropy (not yet collapsed). Low entropy means it has fewer possibilities — choosing it first minimises contradictions:
3. Constraint Propagation (AC-3)
After collapsing a cell, its neighbours may no longer support certain tiles. We propagate by removing tiles from neighbours that have no valid neighbour tile to pair with — similar to the AC-3 arc-consistency algorithm in CSP solving:
4. Backtracking on Contradiction
When propagation empties a cell's possibilities, the current observation is incompatible with the constraints. The classic WFC approach restarts from scratch; advanced versions implement chronological or dependency-directed backtracking:
5. Overlapping vs Tiled Model
Tiled Model
Explicit set of T distinct tiles with hand-authored or extracted pairwise adjacency rules. Fast, full creative control. Used for most game level generators.
Overlapping Model
Learn N×N patterns from a sample image. Every N×N window in the output must appear in the input. Produces textures that look like the example. Slower but zero manual rule authoring.
3D Extension
6-directional neighbours (±X ±Y ±Z). Same algorithm, larger adjacency tables. Used in voxel worlds (Caves of Qud WFC rooms, shape synthesis research).
Wave Function Collapse Jr.
Simplified version using only tile counts and pair frequencies. Faster but produces less complex structures. Good enough for terrain textures.
6. Extracting Adjacency Rules from Examples
7. JavaScript Implementation
// Minimal WFC tiled model
class WFC {
constructor(W, H, tiles, rules, weights) {
// rules[dir][tileA][tileB] = bool; dirs: 0=N 1=E 2=S 3=W
this.W = W; this.H = H;
this.T = tiles.length;
this.rules = rules;
this.weights = weights ?? new Array(this.T).fill(1);
this._init();
}
_init() {
// Each cell starts with all tiles possible
this.grid = Array.from({length: this.H}, () =>
Array.from({length: this.W}, () =>
new Set(Array.from({length: this.T}, (_,i) => i))
)
);
}
entropy(x, y) {
const poss = this.grid[y][x];
if (poss.size === 1) return 0;
const total = [...poss].reduce((s,t) => s + this.weights[t], 0);
return [...poss].reduce((s,t) => {
const p = this.weights[t] / total;
return s - p * Math.log2(p);
}, 0);
}
observe() {
// Find min-entropy uncollapsed cell
let minH = Infinity, cx = -1, cy = -1;
for (let y = 0; y < this.H; y++) {
for (let x = 0; x < this.W; x++) {
const sz = this.grid[y][x].size;
if (sz <= 1) continue;
const h = this.entropy(x, y);
if (h < minH) { minH = h; cx = x; cy = y; }
}
}
if (cx === -1) return 'done';
// Weighted random tile selection
const poss = [...this.grid[cy][cx]];
const total = poss.reduce((s,t) => s + this.weights[t], 0);
let r = Math.random() * total;
const chosen = poss.find(t => (r -= this.weights[t]) <= 0) ?? poss[poss.length-1];
this.grid[cy][cx] = new Set([chosen]);
return this.propagate(cx, cy);
}
propagate(startX, startY) {
const queue = [[startX, startY]];
const DX = [0,1,0,-1], DY = [-1,0,1,0];
const OPP = [2,3,0,1); // opposite direction index
while (queue.length) {
const [cx,cy] = queue.shift();
for (let d = 0; d < 4; d++) {
const nx = cx+DX[d], ny = cy+DY[d];
if (nx<0||ny<0||nx>=this.W||ny>=this.H) continue;
const nbPoss = this.grid[ny][nx];
const before = nbPoss.size;
for (const tNb of [...nbPoss]) {
// Remove tNb if no tile in current cell supports it
const ok = [...this.grid[cy][cx]].some(tC => this.rules[d][tC][tNb]);
if (!ok) nbPoss.delete(tNb);
}
if (nbPoss.size === 0) return 'contradiction';
if (nbPoss.size < before) queue.push([nx, ny]);
}
}
return 'ok';
}
run(maxRestarts = 50) {
for (let attempt = 0; attempt < maxRestarts; attempt++) {
this._init();
let status;
do { status = this.observe(); } while (status === 'ok');
if (status === 'done') return true;
}
return false; // failed
}
result() {
return this.grid.map(row => row.map(s => [...s][0]));
}
}
8. Applications and Extensions
Level Generation
Dungeons, caves, city blocks with consistent styles. Noita uses a procedural tiling system inspired by WFC for its pixel world. Hand-curate a small sample, generate unlimited maps.
Texture Synthesis
Overlapping model on a texture sample produces seamless, infinite textures matching the input statistics. Used for terrain, foliage, brick patterns sans obvious repetition.
3D Shape Synthesis
Academic work (Paul Merrell 2007 predates WFC coinage) generates 3D building facades and urban layouts by learning voxel adjacency from example 3D models.
Music Generation
Apply WFC to musical note sequences: rules encode harmonic compatibility, rhythm patterns. Generates music that follows learned stylistic constraints bar by bar.
Recent extensions include constraint-guided WFC (user paints "must be water" / "must be wall" cells before generation), hierarchical WFC (generate room layout first, then fill each room), and animated WFC (generate tilemaps that also animate correctly by extending rules to include time dimension).