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 the Procedural Level Generator — configure algorithm, size, and seed, then export TMX, Godot .tscn, JSON, or PNG preview. Pair exported tilemaps with a Bitmask Autotile terrain set so walls and floors render without manual tile fixing.
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;
}
Hybrid Design: Procgen + Handcrafted
Most shipped procgen games blend algorithms with authored content:
- Hand-fixed spawn and boss rooms — pacing anchors players trust
- Procgen middle — replayability lives here
- Template chunks — pre-authored 8×8 modules stitched by BSP
Document the split in your GDD. The generator prototypes the procgen portion; designers mark SPAWN, EXIT, and BOSS cell types in export JSON before engine import.
Metroidvania and Platformer Constraints
Room-based BSP alone is not enough for platformers — verticality and jump reach matter.
Add these validation passes after generation:
- Reachability graph: Flood fill from spawn with allowed moves (walk, jump height, dash if applicable)
- Golden path: A* from spawn to exit — if no path exists, discard seed
- Drop distance: Max fall height before kill plane — flag rooms that violate
The Procedural Level Generator runs path validation on exported layouts so you do not import impossible maps into Godot.
Seeding and Reproducibility
// Seeded RNG — same seed = same layout
function mulberry32(seed) {
return function() {
seed |= 0; seed = seed + 0x6D2B79F5 | 0;
let t = Math.imul(seed ^ seed >>> 15, 1 | seed);
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
return ((t ^ t >>> 14) >>> 0) / 4294967296;
};
}
Share seeds with playtesters ("try seed 482910") for reproducible bug reports. Store world_seed in save data for daily challenge modes.
Godot 4 Export Workflow
After generating and validating a layout:
- Export JSON or Godot .tscn from the tool
- Instance under a
Node2Dparent with yourTileMapLayerscripts - Apply terrain painting — see Godot Autotile Guide
- Place enemies on floor tiles tagged in export metadata
Full script example in The Godot 4 2D Pipeline.
Placing Enemies, Loot, and XP
Tile type alone does not make a level fun. Post-generation content passes:
| Cell tag | Typical placement |
| --- | --- |
| FLOOR dead-end | Elite enemy or chest |
| FLOOR main path | Trash mobs, tutorial pickups |
| ROOM_CENTER | Mini-boss or shrine |
| Distance from spawn | Scale enemy tier and XP rewards |
Simulate loot drops against expected clears-per-hour once enemy counts are known.
Using the Procedural Level Generator
Open the Procedural Level Generator, select your algorithm, configure parameters (map size, room count, fill probability), and generate. Regenerate until pacing feels right, then export TMX for Tiled, JSON for custom loaders, or Godot scene fragments.
Related Guides
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: Procedural Level Generator →