How Brogue and Noita Use Cellular Automata
Two of the best-known uses of cellular automata in games trace back to a magazine column. In October 1970, Martin Gardner’s “Mathematical Games” column in Scientific American introduced John Conway’s Game of Life, a grid where every cell lives or dies by counting its neighbors. Today Brogue uses the same idea to grow the caves and lakes in its dungeons, and Noita uses a falling-sand version of it to make every pixel on screen pour, pile and flow.
The two games also show the two jobs a cellular automaton can do. Brogue shapes a level once, while it’s being generated. Noita simulates a world while you play. Below, we’ll take both apart: you’ll grow a Brogue-style cave in a live lab, see why Noita updates its world from the bottom up, and leave with about 50 lines of JavaScript you can drop into your own level generator. If you want the full build, our Cellular Automata tutorial covers it step by step.
The Shared Idea: Every Cell Watches Its Neighbors
A cellular automaton is a grid plus one rule. Each cell looks at the 8 cells touching it (the Moore neighborhood), and the rule decides what it becomes next. Conway’s whole rulebook fits on a sticky note: a dead cell with exactly 3 live neighbors is born, and a live cell with 2 or 3 live neighbors survives. That’s written B3/S23, for born and survives.
Hold on to that notation, because Brogue’s rules use it too. The numbers say how many live neighbors it takes to be born or to stay alive. What changes between games is when the rule runs:
- Shaping (Brogue): run the rule a handful of times while generating a level, then freeze the result. Every cell updates at once, reading from a copy of the previous grid.
- Simulating (Noita): run the rules continuously while the game is playing. Cells hold materials that move, and the order you update them in becomes a design decision.
How Brogue Grows Its Caves
Brogue is a classic roguelike, maintained today as the open-source Brogue: Community Edition. Its caves and lakes are grown as cellular automaton “blobs,” and the whole recipe is readable in the Brogue CE source, in a file called Grid.c.
The Recipe: 55%, Five Rounds, One Survivor
- Seed: mark about 55% of the cells in a rectangle as open, at random.
- Smooth: run 5 rounds of the rule. Caves use B678/S45678: a closed cell opens if 6 or more of its 8 neighbors are open, and an open cell stays open if at least 4 are.
- Measure: flood-fill every blob using 4-way connections (up, down, left and right), and keep only the largest. Everything else gets filled back in.
- Check: if the winning blob is too small, throw it away and roll again.
One detail trips people up: in Brogue the live cells are the open blob, the floor or the lake, not the walls. Most cave tutorials, ours included, treat walls as live. Brogue also counts cells beyond the edge of the grid as closed, so blobs pull away from the border instead of bleeding into it.
Here’s a mini-lab built from our tutorial’s code, with Brogue’s numbers plugged in. It starts on the cave rule, a 55% fill and 5 rounds.
Lab: Brogue-Style Blob
👉 Try this: press Watch It Grow and watch the static collapse into islands. Then switch to All Blobs: every color is a separate cave, and Brogue throws away all but one. Push Min size to 70% and watch the attempt counter climb as the generator rerolls. Finally, try the Lake rule and see how much more open space survives.
Why Brogue’s Rules Work
Look at the cave rule again. B678 makes it hard for rock to open up, because a closed cell needs 6 open neighbors. S45678 lets open cells survive with just 4. Each round eats away thin strands and stray specks while solid areas hold their shape, so after five rounds you’re left with winding, smooth-edged caves. On the lab’s 96×60 grid, the cave rule usually leaves around 20 blobs, and the biggest covers roughly a sixth of the map.
The lake rule, B5678, lets a closed cell open with only 5 open neighbors, so the open space holds together in a few big, soft-edged bodies. Here’s a fun detail: B5678/S45678 is also how the classic “4-5 rule” from RogueBasin is written, the cave rule our tutorial builds, except that version treats walls as live. This rule happens to be its own mirror image. Flip every cell, run it, flip back, and you get the same grid. A 55% open fill is also a 45% rock fill, RogueBasin’s suggested starting point. We checked it in code, and 50 seeds out of 50 matched cell for cell. Brogue’s lakes and the textbook cave are the same automaton seen from opposite sides.
The real lesson is in the last two steps. Smoothing never guarantees one connected space, and Brogue doesn’t try to repair the leftovers. It keeps the biggest blob and rerolls when that blob is too small. That’s brute force, but it’s cheap: a few rounds on a small grid cost almost nothing, so trying again is easier than fixing. When you can’t afford to throw space away, our tutorial’s regions lab shows the alternative: carving tunnels between every cave.
How Noita Simulates Every Pixel
Noita goes the other way. Instead of shaping a level once and freezing it, it simulates its world pixel by pixel while you play: sand piles up, liquids pour, and the terrain itself can collapse. Developer Petri Purho walked through the technology in his GDC 2019 talk, “Exploring the Tech and Design of ‘Noita’,” and Nolla Games described the details in a 2019 interview with 80.lv.
Simple Rules for Every Material
Every pixel is a material with a short list of moves. In the 80.lv interview, the developers explain that a liquid pixel first checks whether it can move down, and if it can’t, it tries sideways. Heavier materials sink through lighter ones because the game compares densities before it swaps two pixels, and gases behave like upside-down liquids. That’s the core of a falling-sand game: no fluid equations, just local checks repeated for every pixel that’s moving.
Why Noita Updates From the Bottom Up
Update order matters far more here than in Brogue. Brogue updates every cell at once from a copy of the grid. Noita moves pixels in place, and the developers say the world has to be updated from the bottom up.
Picture scanning from the top instead. A grain of sand moves down one row, then the scan reaches that row and moves the same grain again, and again, so it teleports to the floor in a single frame. Scanning upward means a falling grain moves at most once per frame. The sandbox in our tutorial uses the same bottom-up scan, plus a “moved this frame” flag and a scan direction that alternates left and right so piles don’t lean to one side.
Scaling Up: Chunks, Dirty Rectangles and a Checkerboard
A whole world of pixels is a lot of work, so Noita works hard to skip most of it. According to the 80.lv interview:
- 64×64 chunks: the world is divided into small chunks.
- Dirty rectangles: each chunk tracks a rectangle around the pixels that need updating, so anything that isn’t changing is skipped.
- A four-pass checkerboard: to spread the work across CPU threads, each pass updates every other chunk in a checker pattern, and a pixel can move up to 32 pixels outside its own chunk. Chunks updated in the same pass are never close enough to touch the same pixels, so the threads don’t need locks. Four passes cover the whole world.
That’s the part of Noita that’s hard to copy and easy to learn from: the rules are tiny, and most of the engineering goes into not running them.
Brogue vs Noita at a Glance
| Brogue | Noita | |
|---|---|---|
| Job | Shapes caves and lakes during level generation | Simulates the world while you play |
| When it runs | 5 rounds, then the result is frozen | Continuously, on every chunk that’s changing |
| Cell states | Open or closed | Many materials: sand, liquids, gases, solids |
| Update style | All at once, from a copy of the grid | In place, from the bottom up |
| Cleanup | Keep the largest blob, reroll if it’s too small | None needed: the mess is the game |
| Cost | Tiny, and paid once per blob | Needs chunks, dirty rectangles and threads |
Other Games That Use Cellular Automata
- Dwarf Fortress: Tarn Adams described its fluid system as “a specialized cellular automata” in a 2008 interview, now archived at Game Developer.
- Sandboxels: R74n’s falling-sand toy started in the browser in December 2021 and launched on Steam in May 2025. It’s a good reminder that you don’t need Noita’s engine to start: a browser canvas is enough.
Not every organic-looking cave is a cellular automaton, though. Minecraft’s cheese, spaghetti and noodle caves come from 3D noise and carvers, not CA. When you read that a game “uses cellular automata,” check the source.
Build a Tiny Brogue Blob Yourself
Here’s the generator that runs the lab above, unedited. It leans on two helpers from the tutorial, countNeighbors (Lab #1) and findRegions, the 4-way flood fill (Lab #3), plus the SeededRandom class from our Perlin noise tutorial, so the same seed always grows the same blob.
const OPEN = 1;
const CLOSED = 0;
// Rules as "how many of my 8 neighbors must be open?" In Brogue the live cells are the open blob.
const CAVE_RULE = { birth: [6, 7, 8], survive: [4, 5, 6, 7, 8] };
const LAKE_RULE = { birth: [5, 6, 7, 8], survive: [4, 5, 6, 7, 8] };
function seedBlob(width, height, rng, fill) {
const grid = new Uint8Array(width * height);
for (let i = 0; i < grid.length; i++) {
grid[i] = rng.nextFloat() < fill ? OPEN : CLOSED;
}
return grid;
}
function blobRound(grid, width, height, rule) {
const next = new Uint8Array(width * height);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const i = y * width + x;
const open = countNeighbors(grid, width, height, x, y, CLOSED); // off-map counts as closed
const allowed = grid[i] === OPEN ? rule.survive : rule.birth;
next[i] = allowed.includes(open) ? OPEN : CLOSED;
}
}
return next;
}
function largestBlob(grid, width, height) {
let best = [];
for (const blob of findRegions(grid, width, height, OPEN)) {
if (blob.length > best.length) best = blob;
}
return best;
}
function growBlob(width, height, rng, rule, fill = 0.55, rounds = 5, minSpan = 0.5, maxAttempts = 100) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
let grid = seedBlob(width, height, rng, fill);
for (let r = 0; r < rounds; r++) {
grid = blobRound(grid, width, height, rule);
}
const blob = largestBlob(grid, width, height);
let minX = width, maxX = -1, minY = height, maxY = -1;
for (const cell of blob) {
const x = cell % width;
const y = (cell - x) / width;
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
// Too small? Throw it away and roll again, like Brogue does.
if (maxX - minX + 1 >= width * minSpan && maxY - minY + 1 >= height * minSpan) {
return { grid, blob, attempt };
}
}
return null;
}
To use it in a game, call growBlob(40, 24, new SeededRandom(seed), CAVE_RULE) and stamp the cells in blob into your level wherever the shape fits. Swap in LAKE_RULE and fill those cells with water instead of floor. The maxAttempts cap is our addition. Brogue simply keeps rolling until it gets a blob it likes.
Companion tutorial
Build the whole cave generator yourself
- Four live labs: the Game of Life, the 4-5 cave rule, flood-fill cleanup with tunnels, and Perlin-seeded caves
- A falling-sand sandbox with sand, water, stone, wood and fire
- Commented code in JavaScript, C# and C++, plus pseudocode
What This Means for Indie Devs
Steal Brogue’s attitude toward failure. A generator doesn’t have to get it right every time. When the check is cheap and the reroll is cheap, roll again. “Keep the largest, reject it if it’s too small” takes a handful of lines, and it removes a whole class of unreachable pockets.
Shaping is nearly free; simulating is a commitment. A few CA rounds at generation time cost almost nothing on a roguelike-sized map. Running materials while the game plays is a different project, and most of what Noita’s developers describe is chunks, dirty rectangles and threads, not the rules. Start with the first, and add the second when your game is really about it.
Choose your live cells on purpose. Brogue makes the open space live, and most tutorials make the rock live. The math can be identical, but the mental model isn’t. Pick the one that matches what you’re designing: the space players walk through, or the rock around it.
Small rules, big worlds. That’s why a grid game from 1970 still shows up in new games: it costs a few lines, and it hands you shapes no designer would draw. Which game should we take apart next? Tell us in the comments.
Sources & Further Reading
- Brogue CE source code,
Grid.c(the blob generator) - Exploring the Tech and Design of ‘Noita’ (Petri Purho, GDC 2019)
- Noita: a Game Based on Falling Sand Simulation (80.lv interview with Nolla Games, 2019)
- Cellular Automata Method for Generating Random Cave-Like Levels (RogueBasin, original article by Jim Babcock)
- Cellular Automata in Game Development (our full tutorial, with four labs and a falling-sand sandbox)

This Post Has 0 Comments