← All posts

Procedural Dungeon Generation in HTML5 Canvas Games

Learn how to build procedural dungeon generation in HTML5 Canvas using BSP trees and cellular automata with practical performance tips.

If you have ever stared at an endless array of handcrafted browser game levels and thought, "Cor, I could code this better in my sleep," then welcome to the wonderful, chaotic world of procedural dungeon generation.

Building levels on the fly using JavaScript and the HTML5 Canvas API is one of those rite-of-passage projects. It sits right alongside writing a dodgy physics engine and pretending you understand vector math. But why do it? Because nothing beats the smug satisfaction of watching your game spin up a brand-new, labyrinthine dungeon every single time a player hits reload, completely free of bulky asset packs or pre-rendered maps.

Recent technical breakdowns lighting up developer subreddits and indie dev Discord channels suggest a massive resurgence in lightweight, browser-first roguelikes. Developers are stripping away heavy WebGL engines in favour of pure Canvas 2D contexts, chasing ultra-fast load times and buttery-smooth frame rates. If you want to build browser games that load instantly and keep players hooked run after run, mastering procedural generation is your golden ticket.


What is Procedural Dungeon Generation?

Entity Definition: Procedural Dungeon Generation is an algorithmic method of automatically creating game levels using mathematical rules, pseudorandom number generators (PRNG), and spatial data structures rather than manual design.

In plain English, it means writing code that plays architect so you do not have to. Instead of drawing fifty different rooms by hand, you write a script that rolls dice behind the scenes, carves out corridors, and places loot chests where the algorithm reckons players will least expect an ambush.

For browser games, this approach keeps file sizes microscopic. Your entire game bundle stays tiny enough to load on a sketchy mobile connection while sitting on the tube, while offering practically infinite replay value.


Core Algorithms Compared: BSP Trees vs. Cellular Automata

When diving into browser game mechanics, you will generally run into two heavy-hitting algorithms for dungeon layout. Let us break down how they stack up against each other for HTML5 Canvas implementations.

FeatureBinary Space Partitioning (BSP)Cellular Automata
Best ForRoom-and-corridor dungeons (Castlevania-style)Cave systems, organic tunnels, open caverns
predictabilityHigh structure; splits a bounding box recursivelyLow structure; emergent chaos based on neighbor counts
Corridor LinkingRequires explicit pathfinding (like A*) or recursive joiningNaturally connected organic paths (with flood-fill checks)
Canvas Render CostLow; draws uniform rectangular tiles efficientlyMedium; requires double-buffering grid states for updates

Method 1: Binary Space Partitioning (BSP) Trees

Binary Space Partitioning is the tidy-freak approach to dungeon building. Imagine taking a massive piece of graph paper and slicing it down the middle, either vertically or horizontally. Then, you take those two pieces and slice them again. And again.

Once your bounding box looks like a piece of abstract art drawn by an aggressive toddler, you turn the smallest sub-divisions into rooms and connect their centres with corridors.

Conceptual Blueprint

1. Define a root container representing your entire map canvas grid.

2. Recursively split the container into smaller sub-nodes until they reach a minimum room size threshold.

3. Create a rectangular room inside each leaf node.

4. Walk back up the tree, connecting sibling nodes with horizontal and vertical hallways.

Here is a quick snippet demonstrating how you might recursively split nodes in JavaScript:


class BSPNode {
  constructor(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.left = null;
    this.right = null;
    this.room = null;
  }

  split() {
    if (this.left || this.right) return false;

    // Decide whether to split horizontally or vertically
    const horizontal = Math.random() > 0.5;
    const maxDimension = (horizontal ? this.height : this.width) - 10;

    if (maxDimension < 20) return false; // Too small to split further

    const splitPos = Math.floor(Math.random() * (maxDimension - 10)) + 10;

    if (horizontal) {
      this.left = new BSPNode(this.x, this.y, this.width, splitPos);
      this.right = new BSPNode(this.x, this.y + splitPos, this.width, this.height - splitPos);
    } else {
      this.left = new BSPNode(this.x, this.y, splitPos, this.height);
      this.right = new BSPNode(this.x + splitPos, this.y, this.width - splitPos, this.height);
    }

    return true;
  }
}

Method 2: Cellular Automata for Cave Systems

If your browser game demands creepy subterranean caves rather than sterile stone dungeons, Cellular Automata is your best mate. Inspired by Conway’s Game of Life, this method relies on simple neighborhood rules to simulate natural erosion and growth.

The Automata Workflow

1. Random Noise Generation: Fill your 2D grid array randomly, setting roughly 40% to 45% of tiles as walls and the rest as open floor space.

2. The Birth/Death Iteration: Loop through every tile. Count how many wall tiles exist within its immediate 3x3 radius. If a tile has more than four wall neighbours, turn it into a wall. Otherwise, make it a floor.

3. Smooth and Repeat: Run this smoothing pass four or five times until jagged noise transforms into organic cave networks.


Performance Tips for HTML5 Canvas Rendering

Generating complex maps is only half the battle. If your JavaScript loop takes half a second to calculate room placements, your browser game will stutter like a cheap lawnmower. Keep these performance golden rules in mind:

  • Offscreen Pre-Rendering: Never draw individual tile grids straight to the active canvas context inside your main game loop. Instead, generate the dungeon once onto an offscreen canvas or 2D array, then blit the static map layer in a single drawImage() call.
  • Chunk Your Calculations: If you are building giant maps, wrap your generation logic inside requestAnimationFrame slices or Web Workers. This stops the dreaded browser "Page Unresponsive" warning from popping up while your poor CPU crunches numbers.
  • Keep Bitmasks Clean: Store your map data in flat typed arrays (Uint8Array) rather than nested arrays of objects. Your browser's garbage collector will thank you, and memory overhead will plummet.

Key Takeaways

  • BSP Trees excel at structured, room-and-corridor layouts perfect for classic dungeon crawlers.
  • Cellular Automata provide organic, winding cave systems through simple neighbor-counting rules.
  • Offscreen Canvas rendering is non-negotiable for maintaining high frame rates in browser-based tile games.
  • Typed arrays keep memory overhead low and prevent GC pauses during map generation.

Thanks for reading. Browse more from the Wobblox blog, or jump straight into all 100 free games.