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.
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.
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.
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.
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.
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.
Weighted A* corridors
Weighted A* routes a corridor along each chosen link, carving the hallways that join the rooms.
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.
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.