Vestro.
▪ P—04 · Showcase / live demo

Collapse.

A procedural dungeon generator I wrote in C++ and WebAssembly — the kind of system I build for games, here as a side project you can run live. Type a seed and watch it solve a fresh, guaranteed-playable level, one tile at a time.

C++20 WebAssembly Emscripten Web Worker Canvas 2D
Jun 2026 Runs in your browser Live demo ↓
01 — What this is

I spend my days building gameplay systems in Unreal Engine and C++, and procedural generation is one of those problems that keeps turning up. Over the years I’ve built level and content generators with everything from Monte Carlo methods to constraint solvers. Collapse is a hobby project — a weekend spent on a clean, self-contained showcase of one of those approaches: wave-function collapse, built the way I’d actually want it in production.

I set myself a few non-negotiables going in: the same seed produces the same dungeon on any machine, native or in the browser; every level is finishable; and it stays fast enough to watch build live. Determinism is the one that quietly bites people, because floating-point results don’t agree across platforms and compilers, so I kept floats out of the decision path entirely. Tile choices run on quantised integers, the geometry on exact integer predicates, and a checksum in CI replays a few hundred seeds through both the native and WebAssembly builds, failing on a single-bit difference.

The room graph is built on a Delaunay triangulation, a well-known minefield of collinear points, near-degenerate triangles, and overflow in the predicates. I’ve been caught by all three before, so the implementation skips the usual bounding-triangle shortcut for a ghost-vertex formulation: a single symbolic “point at infinity” that stands in for the boundary and keeps the result exact and hole-free by construction. The coordinates are bounded tightly enough that the in-circle predicate provably never overflows a 64-bit integer, so there’s no slow big-number arithmetic in the hot path.

Performance drove a lot of the structure. The textbook solver re-scans every cell on every step, which is fine for a toy and falls over at scale, so cell selection runs off a lazy priority queue. That alone is the difference between a 256-cell grid taking eleven seconds and taking a quarter of a second. And since a level you can’t finish is worthless, every result is flood-filled to confirm the rooms form one connected space with the entrance and exit mutually reachable; if they don’t, the generator re-rolls the geometry and tries again.

All of it is C++ compiled to WebAssembly and run in a Web Worker, streaming each step back to the canvas so the page never stutters, even mid-solve. Hit Run below and watch it work.

02 — Try it

Generate a dungeon and watch it come together. Switch biomes, resize the grid, tune it in the advanced controls, scrub through the build, or flip on the graph to see the structure underneath.

collapse / generator 16.6ms · 60fps
Press Run to generate a dungeon ~48 × 48 grid · finishes in well under a second
starting…
Generator controls
Biome click to cycle
Seed
Grid size square
Playback drag to scrub the build
Advanced parameters shape the dungeon
Room size cells per side
Loopiness 12
Straightness 3
Room density auto
idle — press Run
msGeneration time
cells/sThroughput
msWorst page frame
Rooms placed
Rooms Corridors Graph edges (toggle)
03 — How it works

Four stages, run in order, every choice driven by the seed. Flip Show graph in the demo to see the room graph and the chosen connections drawn over a finished map.

▪ The pipeline · four stages
Stage 01Rooms

Place the rooms

The generator scatters rooms of varied sizes across the grid, rejecting any that would overlap — so you start with distinct spaces, not noise.

Stage 02Graph

Connect them

Each room becomes a point. A Delaunay triangulation proposes every sensible link, a minimum spanning tree keeps every room reachable, then a few extra edges go back in so the map loops instead of forcing one path.

Stage 03Carve

Weighted A* corridors

Weighted A* routes a corridor along each chosen link, carving the hallways that join the rooms.

Stage 04Collapse

Theme the tiles

Now wave-function collapse themes every cell of the carved map under adjacency rules — floors, walls, doors — restarting cleanly if it paints itself into a corner. It's the tile-by-tile solve you watch fill in, and the step the project is named after.

Every stage runs in C++ inside the worker and streams its progress to the canvas. That's why the page stays smooth even when the solver has to throw an attempt away and start over.

04 — Notes

Built in C++20, compiled to WebAssembly with Emscripten, and run off the main thread in a worker — the canvas just draws what it streams back.

A weekend's work, and a clean showcase of one way to generate a dungeon out of the several procedural approaches I've worked with over the years. If you want to talk Unreal, procedural generation, or engine work, the contact link's right below.