2D Procedural Level Generation: BSP, Cellular Automata & Interactive Prototyper
Procedural level generation is the technique of creating game levels algorithmically rather than by hand. Every run of Spelunky, The Binding of Isaac, or Dead Cells produces a different layout because the levels are generated at runtime from a set of rules, not loaded from a fixed file. The result is virtually unlimited replayability from a relatively small codebase. This guide covers the core algorithms — BSP dungeon generation, cellular automata caves, and Perlin noise terrain — and how to prototype them instantly with a free browser tool.
Generate 2D levels in your browser with Jaconir 2D Procedural Level Generator — configure the algorithm, size, and parameters, and export to a tile array ready to use in your game engine.
Why Use Procedural Generation?
- Infinite replayability: Each run produces a unique layout
- Reduced content creation time: One algorithm replaces dozens of hand-made maps
- Scalable content: Change parameters to produce easy, medium, or hard layouts
- Surprise and discovery: Neither the player nor designer knows what each run contains The tradeoff is control — handcrafted levels offer precise pacing and storytelling that procedural levels cannot. Most games that use procgen combine it with curated setpieces, fixed boss arenas, or hand-designed starting areas.
Algorithm 1: BSP Dungeon Generation
Binary Space Partitioning (BSP) is the most common algorithm for dungeon generators. It produces room-based layouts with reliable connectivity — every room is reachable.
How it works:
- Start with the full map area as a single rectangle
- Split it in half (randomly horizontal or vertical)
- Recursively split each half until regions are small enough for a single room
- Place a room inside each leaf region (with random padding)
- Connect sibling rooms with corridors, working back up the BSP tree
Result: Clean rectangular rooms connected by corridors. Predictable but feels natural. Best for dungeon crawlers, roguelikes with loot rooms, and any game where room identity matters.
// Simplified BSP split
function splitNode(node, minSize) {
const splitHorizontal = Math.random() > 0.5;
const splitPos = splitHorizontal
? Math.floor(node.y + minSize + Math.random() * (node.height - minSize * 2))
: Math.floor(node.x + minSize + Math.random() * (node.width - minSize * 2));
if (splitHorizontal) {
node.left = { x: node.x, y: node.y, width: node.width, height: splitPos - node.y };
node.right = { x: node.x, y: splitPos, width: node.width, height: node.y + node.height - splitPos };
} else {
node.left = { x: node.x, y: node.y, width: splitPos - node.x, height: node.height };
node.right = { x: splitPos, y: node.y, width: node.x + node.width - splitPos, height: node.height };
}
}
Algorithm 2: Cellular Automata Caves
Cellular automata produces organic cave-like spaces that feel natural and irregular — the opposite of BSP's clean rectangles. Used in games like Caves of Qud and many cave-themed roguelikes.
How it works:
- Fill the map randomly — each cell is wall (1) or floor (0) with a set probability (typically 45% floor)
- Apply the automata rule repeatedly (4–6 iterations): if a cell has 4 or more wall neighbours, it becomes a wall; otherwise it becomes a floor
- The random noise resolves into smooth, organic cave shapes
- Remove isolated regions to ensure connectivity
function cellularStep(grid, width, height) {
const next = grid.map((row, y) =>
row.map((cell, x) => {
let wallCount = 0;
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
if (dx === 0 && dy === 0) continue;
const nx = x + dx, ny = y + dy;
if (nx < 0 || nx >= width || ny < 0 || ny >= height) wallCount++;
else if (grid[ny][nx] === 1) wallCount++;
}
}
return wallCount >= 5 ? 1 : 0;
})
);
return next;
}
Algorithm 3: Perlin Noise Terrain
Perlin noise generates smooth, continuous random values that produce natural-looking terrain — rolling hills, gradual elevation changes, coherent biome shapes. Used in Minecraft, Terraria, and virtually every open-world game.
How it works: Perlin noise produces a value between -1 and 1 for any (x, y) coordinate. Values above a threshold = solid ground, below = open space. Layering multiple noise functions at different scales (octaves) produces terrain with both large features and fine detail.
// Using a noise library (simplex-noise)
import { createNoise2D } from 'simplex-noise';
const noise2D = createNoise2D();
function generateTerrain(width, height) {
return Array.from({ length: height }, (_, y) =>
Array.from({ length: width }, (_, x) => {
// Layer multiple octaves
const n = noise2D(x * 0.05, y * 0.05) * 0.6
+ noise2D(x * 0.1, y * 0.1) * 0.3
+ noise2D(x * 0.2, y * 0.2) * 0.1;
return n > 0.1 ? 1 : 0; // 1 = solid, 0 = air
})
);
}
Ensuring Connectivity
The most common problem with procedural levels is generating unreachable areas. After generating, always run a flood fill from the player start position and check that all required areas are reachable. If not, regenerate or connect isolated regions with a corridor.
function floodFill(grid, startX, startY) {
const visited = new Set();
const queue = [[startX, startY]];
while (queue.length) {
const [x, y] = queue.shift();
const key = `${ x }, ${ y }`;
if (visited.has(key) || grid[y]?.[x] !== 0) continue;
visited.add(key);
queue.push([x+1,y],[x-1,y],[x,y+1],[x,y-1]);
}
return visited;
}
Using the Procedural Level Generator
Open Jaconir 2D Procedural Level Generator, select your algorithm, configure the parameters (map size, room count, fill probability), and generate. The tool outputs a 2D tile array you can paste directly into your game engine. Regenerate until you get a layout that fits your game's needs, then export.
FAQ
Which algorithm should I use for my game?
BSP for dungeon crawlers with distinct rooms and loot placement. Cellular automata for cave exploration games with organic layouts. Perlin noise for open-world or Terraria-style terrain. Most games benefit from combining algorithms — use BSP for room layouts and cellular automata for cave sections.
How do I place enemies and items in procedurally generated levels?
After generating the tile map, identify valid placement zones (floor tiles away from the player start, dead-end rooms, etc.) and randomly place content within those zones. Weight placements by distance from start for difficulty scaling.
Can I seed procedural generation for reproducible levels?
Yes — all major random number generators accept a seed value. Using the same seed always produces the same level, enabling level sharing via seed codes (like Minecraft seeds).
Conclusion
Procedural generation turns a single algorithm into effectively unlimited content. BSP gives you reliable dungeon layouts, cellular automata gives you organic caves, and Perlin noise gives you infinite terrain. Combining them gives you a complete world generation system. Start simple with one algorithm and expand from there.
Generate your first level: Jaconir 2D Procedural Level Generator →