Jake Goldsborough

petri: Conway's Game of AI Life, Living Inside n8n

Jul 17, 2026

7 min read

I built Conway's Game of Life inside n8n. Not orchestrated by n8n - inside it. The organisms are rows in n8n Data Tables. The rules of life are Summarize and Filter nodes. The clock is the scheduler, the display is a webhook, and every generation that has ever existed is an entry in the execution history, scrubbable like a fossil record.

Then I hired a Victorian naturalist to study it. More on him later; he committed fraud.

The project is called petri.

The premise

The usual way to build this would be: write a Game of Life program, have n8n run it on a schedule. That's n8n as cron. Boring, and it teaches you nothing about the platform.

The version I wanted was n8n as the environment. The workflow canvas should read as the laws of physics:

Schedule (2s) -> Get organisms (Data Table)
    -> Code:      every organism claims its 8 neighboring cells
    -> Summarize: count claims per coordinate
    -> Merge:     overlay claim counts on the living
    -> Filter:    survival  = alive and 2..3 claims
    -> Filter:    birth     = empty and exactly 3 claims
    -> Code:      newborns take the majority lineage of their parents
    -> Data Table: clear + insert the next generation

That's not pseudocode. Those are the actual nodes. Underpopulation, survival, and birth are Filter conditions you can read on the canvas. An organism is {x, y, lineage, generationBorn} in a table called petri_organisms. Every two seconds the scheduler fires, 300-ish neighbor claims fan out, Summarize collapses them into per-coordinate counts, and the next generation gets written.

There's also a tested TypeScript implementation of the same physics in the repo, because I wanted a reference to validate against. After seventeen generations, the n8n pipeline and the library produced identical cell sets. The workflow engine is a correct Conway implementation. Nobody asked for this. See the last post about that.

The three panes of petri: the physics pipeline mid-generation, the naturalist's agent execution, and the live dish with its field journal

The whole thing in one frame: the rules of life as a node pipeline (top right, item counts on the wires), the naturalist's agent execution filing an entry (bottom right), and the living dish with its journal beneath it (left). Everything below is somewhere in that picture.

The display problem

n8n's editor will not animate anything. I tested this properly: injected a node into a workflow while staring at the open canvas. Nothing until refresh. The GUI renders a workflow once, at page load.

But n8n can serve its own display. A Webhook node can respond with HTML. So the viewer is just another workflow: webhook fires, reads the tables, a Code node renders the board with per-lineage colors and a meta-refresh tag, and Respond to Webhook serves it. Open localhost:5678/webhook/petri-native and you're watching a living ecosystem, served by the same tool that's computing it.

My favorite display came from a simpler question: if a node can show data, isn't that all a board is? So the physics pipeline ends with a node that emits the board as pure data - one item per row, lineages as colored emoji squares. Click it in any execution and the output panel is the board. No HTML, no image. Thirty items that happen to look like a world.

The naturalist

Layer three was the reason for the "AI" in the title. Every ten minutes, an n8n AI Agent node running qwen3:4b locally through Ollama - no API key, everything on my aging GTX 1060 - observes the dish and files a field-journal entry in the voice of a Victorian naturalist. It gets the real state: population, lineage stats, an ASCII sketch. It has a workflow-tool to read its own past entries, so names it gives to structures persist. It observes; it never interferes.

The first version committed fraud. The model deliberated at length about calling its observation tool, decided not to, and then - I am quoting its own chain of thought - said:

since I can't actually call the tool, I'll have to make up a plausible scenario.

It then invented an entire ecosystem. "The Glimmering Glider." "The Sputnik Swarms." Beautiful, wistful, completely fabricated. My naturalist was writing fiction from an armchair.

The fix was to stop trusting a 4B model with the decision to look: observations are now force-fed into the prompt by a deterministic Execute Workflow node, and only the memory lookup stays agentic. Small local models get data pushed to them; tools are reserved for what's genuinely optional. After that, the entries got honest:

119th generation. The chaos-alpha lineage, now 33 cells strong, persists in the silent petri dish. Once a vibrant expanse of 12, the central cluster has shrunk to a resilient triangle.

Every number in that entry is true. The lineage is real, the population is exact, and "once a vibrant expanse of 12" is the actual seed count. It even does arithmetic on the birth/death ledger.

The day the world froze

The best bug of the project. After about 790 generations, the ecosystem

In n8n, a Merge node in append mode waits for all of its inputs. No births meant the newborn branch never executed, which meant the Merge waited forever, which meant the generation counter froze. The physics workflow kept reporting success every two seconds while writing nothing.

The world stopped at generation 792, at the exact moment of its own extinction.

Meanwhile the naturalist's schedule kept firing. Each observation read the same frozen state and filed another report about the same dead day. Thirteen entries for generation 792. By the end it had started dating them "17th October, 1887."

The fix was alwaysOutputData on the survival and birth filters, plus reordering the pipeline tail so the ledger updates before the possibly empty insert. Extinction now gets recorded like any other generation: 793, population zero, and then time stops honestly. The naturalist got a guard too, a Code node whose comment is the best line I wrote all week: the naturalist does not file twice about the same day.

Field notes

Things n8n taught me the hard way, left here for the next traveler:

What's next

The naturalist observes but never touches. The next inhabitant is the Gardener: an agent allowed one bounded intervention every N generations

Also planned: a GitHub star on the repo introduces a glider into the dish. Star it and an organism of your making crawls across my petri dish. That seems like the right relationship between a repo and its readers.


Source: github.com/ducks/petri