The Concept
Cellular automata occupy a strange position in computation. The rules are embarrassingly simple. A cell looks at its neighbors and decides its next state based on a fixed table. The complexity is emergent in the fullest sense: it was never programmed in, it arrived on its own.
But before all’at, I got inspired to make this project by looking at the world
map and imaging it made in Cellular Automata. So after brainstorming I decided to make an interactive canvas for drawing maps and terraforming them with painting, erasing, and natural disasters. After talking to the professor, he said that this “game” doesn’t really have much of a goal. So I will take you on a journey of how I made a game that I would actually enjoy playing.
The specific rule set this project uses is B5678/S45678. A dead cell with five or more live neighbors is born. A live cell with four or more live neighbors survives. What this produces, when run on a noisy random seed, is cave systems and islands. The rule set naturally fills voids and thins peninsulas. Run it long enough on a field of random noise and it self-organizes into something that looks geographically plausible, with rounded coastlines and interior lakes. This particular CA flavor has a bias toward landmass, which makes it useful for seeding a world.
The rule is simple: if a pixel is opaque enough (alpha above 128) and dark enough (red channel below 80), it becomes a wall, marked as 1. Everything else becomes open space, marked as 0. The result is a grid that already carries the silhouette and rough geography of the original image, before a single CA rule has fired.
- Birth (B5678): A floor cell turns into a wall if it has 5, 6, 7, or 8 neighboring wall cells.
- Survival (S45678): A wall cell remains a wall if it has 4, 5, 6, 7, or 8 neighboring wall cells.
The crises, Drought and Famine check neighbors and spread probabilistically to adjacent land cells on a timer. Drought uses 8-connectivity at 25% chance per tick, famine uses 4-connectivity at 20%. They follow a simplified CA-adjacent logic, but without the strict synchronous neighbor-count birth/survival table. Plague skips the grid entirely and spreads settlement-to-settlement by distance threshold.
The food system runs its own parallel logic on top of the terrain. Every fertile land cell produces a small food output per frame, and every settlement consumes based on its tier. The ratio between those two quantities is the only metric that matters for whether civilizations grow or collapse. Crises work as a third layer: drought and famine spread cell-to-cell via their own CA-like rules, plague spreads settlement-to-settlement. The world is three overlapping automata running simultaneously, each blind to the others but producing emergent pressures that the player has to navigate.
What Inspired This
The immediate ancestor of this project is a semester of p5.js work that kept returning to the same question: what makes a system feel alive rather than just animated? GALACTIC answered it through particle pressure and charge. DRIFT answered it through steering behaviors and population dynamics. TERRA pushed that question further.
I got stuck on a specific image. In DRIFT, the prey species would sometimes form tight clusters near the food sources, and the predators would circle the periphery. I hadn’t scripted that behavior. It arrived from the interaction of three simple rules: seek food, flee predators, maintain separation. That image of emergent territorial behavior kept sitting in my head after I submitted the project.
The civilizational layer came from reading about stratified populations for my nationalism course. We were studying how national identity forms not as a top-down imposition but as something that accumulates at the edges of infrastructure and shared resource pressure. A hamlet near water is not a hamlet by design; it’s a hamlet because water access makes survival viable there. The settlement placement logic in TERRA works exactly that way. Settlements only spawn on coastal land where sea access meets fertile soil.
The two-mode structure, Steward and Sandbox, came from thinking about what different relationships to a world feel like. In Steward mode the player is a greek god with a budget. Why green you might ask? I return to this in almost all of my blog posts; autonomy, agency, simply because I can. In Sandbox mode the player is a pure demolition artist. The same underlying terrain engine serves both uses, which meant the CA and tool systems had to be agnostic about intent.
The Plan
I sketched the architecture in three layers before improving upon the previous code i had from draft 2. Terrain was the base: a 2D grid of 1s and 0s, updated by the CA rule every eight frames. The parallel grids sat on top of terrain: fertility, crisis state, and crisis age. These are separate arrays that reference the same coordinate space. Settlements were a list of objects that sample both layers on every update tick.
The player-facing tools needed to be implemented as targeted disruptions to this system. Earthquake needed to cure plague along a beam path. Tsunami needed to clear drought along an expanding ring. Volcano needed to destroy terrain, clear famine, and introduce high-fertility land as new volcanic soil hardened. Each tool was designed as a counter to exactly one crisis type, which meant the game had an answer to every problem if you positioned it correctly.
The UI plan was minimal. A mode bar at the top center, a HUD at the top corners for steward mode, a crisis panel at the bottom left for active crises, and a custom cursor that previewed each tool’s area of effect as a ghost overlay before firing.
I also planned the feedback particles early. Founding a hamlet gets a green sparkle. A tier upgrade gets a gold burst. A plague cure gets a green label. A settlement abandonment gets grey smoke. These are the only way the player understands what is happening inside the sim at any given moment.
Step-by-Step:
Milestone 1: Grid and CA
The first thing I got working was the CA step itself. Build a grid, apply the B5678/S45678 rule, render it. This took a day. The trickiest part was the boundary condition. I decided out-of-bounds cells count as land, which biases the CA inward and prevents the edges from eroding to water, which would’ve cut off coastal settlement placement near the canvas border.
Milestone 2: Terrain Generation
Two approaches: image-sampling for Sandbox mode, procedural island for Steward mode. The image sampler reads pixel brightness and opacity to classify land vs water. The procedural island blends a radial gradient with Perlin noise at a 70/50 split and thresholds the combined value at 0.55. Four CA smoothing passes run after generation to round the jagged initial shape into something that feels geographically plausible. Each game generates a different island because noiseSeed() randomizes on startup.
Milestone 3: Settlements
Coastal detection required a four-connectivity check: is this cell land, and does it have at least one adjacent water neighbor? The spawn function samples random grid positions, checks coastality, checks minimum distance from existing settlements, checks that no crisis is active on the tile, and places a hamlet if all conditions pass. The spawn gating on food ratio was a late addition that I’m glad I made. Without it, settlements spawn into starvation immediately if the map is small.


Milestone 4: Food and Crises
The food system was straightforward once the fertility grid was in place. Crisis spreading was harder. Drought uses eight-connectivity spread with a 25% probability per tick. Famine uses four-connectivity with 20% probability (slower and more directional, mimicking how supply disruptions travel along routes). Plague is entirely settlement-to-settlement with a distance threshold and an immunity window to prevent instant re-infection after cure. Getting these three rates balanced so each crisis was dangerous but counterable took a lot of manual tuning.
Here’s a highlight of code i am proud of:
for (let j = 0; j < rows; j++) {
for (let i = 0; i < cols; i++) {
if (crisisGrid[j][i] === 1) {
crisisAge[j][i]++;
if (crisisAge[j][i] >= 2) {
for (let dj = -1; dj <= 1; dj++) {
for (let di = -1; di <= 1; di++) {
if (di === 0 && dj === 0) continue;
let nx = i + di, ny = j + dj;
if (inBounds(nx, ny) && grid[ny][nx] === 1 && crisisGrid[ny][nx] === 0) {
if (random() < 0.25) toAdd.push([nx, ny]);
}
}
}
}
}
}
}
The crisisAge check is doing important work here. A freshly seeded drought tile cannot spread on its first tick. It has to mature for at least two spread intervals before it can infect neighbors. Without that gate, the initial seed cluster would explode outward in one frame and the player would have no reaction window at all. The 25% probability per neighbor per tick means spread is visible but not instant. You can watch the tan discoloration creep across the land in real time, which is the whole point.
Famine uses the same structure but with 4-connectivity only (no diagonals) and a 20% probability, making it slower and more directional. Plague skips the grid entirely. It checks settlement-to-settlement distance and seeds infection directly on nearby objects.
Here’s an old picture of how crises were, this is before I implemented them spreading and stuff.

Milestone 5: Disaster Tools
Earthquake beams move at 0.6 cells per frame along four cardinal directions. At every integer cell the beam tip crosses, it checks for settlements within one grid cell and cures their plague while granting 600 frames of immunity. The tsunami ring expands outward and samples the ring boundary at angular increments, clearing drought on any land cell it touches. The volcano runs a 180-frame timer, erodes a central crater, then stochastically solidifies outer cells as high-fertility land at 6% per frame per cell.
Milestone 6: Steward Mode Full Pass
I wired the energy system, the crisis spawning scheduler, the win condition, and the toast/floating label feedback. The steward mode tutorial runs once per session and front-loads the counter-tool pairings: tsunami clears drought, earthquake breaks plague, volcano ends famine. The crisis panel at the bottom left updates live and tells the player exactly which tool to use. I did not want the player to guess.
Challenges and Struggles
The hardest single problem was making the CA not eat settlements. The CA rule does not know that a given land cell has a house on it. If the terrain naturally converges toward a rule that kills that cell, the settlement disappears from the grid and the next frame the update function finds a settlement sitting on water and removes it. The fix is lockSettlements: after each CA step, the function iterates all settlement grid positions and forces them back to 1. This keeps terrain alive underneath existing settlements while still allowing CA to shape the rest of the map.
The second big struggle was food balance. In early versions, the food ratio crashed to zero almost immediately because a handful of settlements consumed more than the entire island produced. I had to tune FOOD_PER_LAND and TIER_FOOD_NEED in tandem over many test runs. The values in the final version feel natural, but they represent maybe three hours of iterative tuning that is totally invisible to anyone playing the game.
Crisis scaling gave me real trouble too. Early playtests had crises that felt either trivial or instantly fatal. The damage accumulation system (30 points to trigger a tier loss or abandonment) came from thinking about health bars in a different way. Instead of instantaneous damage, crises chip away slowly. Drought does 0.04 per frame. A full drought tile on a hamlet takes about 12 seconds to force abandonment, which is long enough to respond. Getting that number wrong in either direction completely breaks the game feel.
The cursor preview system was finicky. The ghost overlay for each tool uses low-opacity strokes to show the area of effect before the player fires. The earthquake preview draws four lines at the actual beam length. The tsunami preview draws concentric ellipses at the actual ring radii. This sounds simple but it required the preview to use the same parameters as the actual tool, which meant I had to keep those values in sync. When I changed a ring’s max radius I had to remember to update the preview too. I missed this twice.
Reflection and What Comes Next
The thing I’m most satisfied with is the emergent world behavior in Steward mode. I did not script the way crises compound. A drought reduces food production, which slows growth, which makes settlements more fragile when famine hits next. I built three independent systems and their interaction produced a cascade logic that feels scripted but isn’t.
What I want to improve: the crisis visuals feel functional but not beautiful. The drought overlay is a warm tan pulse. Famine is a grey-brown desaturation. These communicate but they don’t carry atmosphere. I want to give drought a particle system, dry cracking particles drifting off land tiles. I want famine to dim the overall palette in the affected region. Plague should feel more visceral; right now the purple ring around a settlement is subtle.
The progression in Steward mode also flattens toward the end. Once you hit three or four cities you have so much food surplus and energy regeneration that crises stop being threatening. A late-game difficulty ramp, faster crisis intervals, compound crises, or a catastrophic endgame event, would fix this.
The biggest gap in the project is sound. The CA is a visual medium by default but each of these events, the founding sparkle, the tier burst, the earthquake beam, have clear sonic signatures that are missing. A procedurally pitched tone that scales with energy level during tsunami expansion would make the game feel dramatically more alive. That’s the first thing I’d add.
References
Inspirations
- Conway’s Game of Life and its B/S rule notation — the conceptual ancestor of the B5678/S45678 terrain rule used here
- DRIFT & GALACTIC (my previous p5.js projects, S2026)
- Kanchan Chandra’s “The Age of New Nationalisms” JTerm course at NYUAD — specifically the framework around stratified resource access as the material basis for collective identity formation
Technical Resources
- The Coding Train — Coding Challenge #85: Game of Life — Daniel Shiffman’s CA walkthrough, useful for understanding the synchronous grid update pattern
- Using Perlin Noise to Generate 2D Terrain and Water— Perlin noise used for island generation, terrain color variation, and lava flicker
- LifeWiki — B5678/S45678 — rule family documentation and behavior classification
AI Disclosure
Claude (Anthropic) assisted with mathematical tuning of several system parameters, particularly the food balance constants (FOOD_PER_LAND, TIER_FOOD_NEED), the crisis damage thresholds, and the probability values for drought and famine spread. I used it iteratively alongside manual playtesting rather than as a one-shot solution. All system architecture, rule design, and structural decisions were my own. Also, UI. I am terrible with UI. AI carried me mostly here and I believe this particular usage is OK because UI design was not a part of our class.