RLE Format Explained: Encode Any Life Pattern in a Tweet

How Conway's Game of Life patterns get packed into a few dozen characters of text — the RLE format, decoded line by line, with live examples pulled from our own pattern library and MCP server.

angen.ai
August 2, 2026
8 min read
how to
rle
cellular automata
mcp
tutorial

RLE Format Explained: Encode Any Life Pattern in a Tweet

Here is the entire Gosper glider gun — 36 living cells arranged in a 36×9 rectangle, the pattern that won a bet and proved Conway's Game of Life could grow without bound:

24bo$22bobo$12b2o6b2o12b2o$11bo3bo4b2o12b2o$2o8bo5bo3b2o$2o8bo3bob2o4bobo$10bo5bo7bo$11bo3bo$12b2o!

That's 99 characters — shorter than most tweets, short enough to paste into a chat message, a URL, or an MCP tool call without a second thought. It's pulled directly from our pattern library's API, not retyped by hand. This is what RLE (Run Length Encoded) format does: it turns a grid of thousands of potential cells into a string proportional to how interesting the pattern is, not how big its bounding box is. A mostly-empty 36×9 rectangle compresses hard; a solid block of noise wouldn't.

RLE isn't something we invented — it's the standard interchange format for Life patterns, originally created by Dave Buckingham and later extended by the Golly project, whose own file-format documentation is the clearest independent reference for the spec. This piece walks through the format itself, then shows exactly how our tools — the pattern library, the simulator's "Custom RLE" import, and the MCP server — read and write it.

The four symbols that do all the work

An RLE pattern body uses exactly four characters:

  • b — a dead cell
  • o — a live cell
  • $ — end of the current row, move to the next
  • ! — end of the pattern

Any run of identical characters can be prefixed with a count: 5o means five live cells in a row, 3b means three dead cells. A glider — the smallest thing in Life that moves — is:

bo$2bo$3o!

Read it left to right: bo is row 0 (one dead cell, one live cell — and since a row can drop trailing dead cells before the $, that's a live cell at column 1 in an otherwise-empty row). 2bo is row 1 (two dead cells, then one live cell — column 2). 3o is row 2 (three live cells, columns 0 through 2). ! ends it. Three rows, five live cells, nine characters. We pulled this exact string live from our own get_pattern MCP tool for patternId: "glider" rather than typing it from memory — it's worth the ten seconds to confirm a tool actually returns what the docs say it returns.

The header line

Above the cell data, a real .rle file (and our simulator's own "Custom RLE" import box) expects one header line:

x = 3, y = 3, rule = B3/S23

x and y are the pattern's bounding-box width and height — mostly there so a paste tool knows how much space to reserve, since the cell data itself is unbounded without it. rule is the cellular automaton's birth/survival rule in the standard B<digits>/S<digits> notation; B3/S23 is Conway's original Life (a cell is born with exactly 3 live neighbors, survives with 2 or 3). Per Golly's own format documentation, whitespace is allowed anywhere in that line except at the very start or mid-token, and the dimensions are optional for loading a pattern but required if you're pasting it into an existing grid, since they define what gets pasted.

Above the header, comment lines start with #. Two conventions carry actual metadata: #N for the pattern's name, #C for a general comment. Our simulator's import placeholder shows a complete example:

#N Glider
#C A small spaceship
x = 3, y = 3, rule = B3/S23
bob$2bo$3o!

(Notice this version spells out the trailing dead cell — bob instead of bo — for row 0. Both are valid; RLE only requires you to drop trailing dead cells before a $ or !, it doesn't forbid writing them out. Real-world RLE files are inconsistent about this, and any correct parser has to handle both.)

One more convention worth knowing if you're staring at a pattern file that looks unusually wide: Golly's own writer tries to keep each line of encoded output to roughly 70 characters for readability when patterns are shared as text files, wrapping mid-pattern with ordinary line breaks that a parser is expected to ignore. That's a formatting nicety, not part of the encoding itself — our own tools emit the whole pattern as one line, which is equally valid and easier to pass around as a single string or JSON field.

What our tools actually do with it

Every pattern-shaping tool on our MCP server speaks RLE natively, both in and out — most accept either a library patternId or a raw RLE string, and every one hands back RLE, not a coordinate list. That's a deliberate choice: handing a language model a well-defined text format it can pattern-match on is a lot more reliable than asking it to reason about a 2D grid of coordinates directly. A few of these, tested live against production for this piece:

get_pattern — fetch any library pattern as RLE:

{ "rle": "bo$2bo$3o!", "width": 3, "height": 3, "cellCount": 5 }

create_grid_pattern — tile a pattern into rows and columns, RLE in, RLE out. Two gliders, 2 columns × 2 rows, 3 cells of spacing:

{ "rle": "bo5bo$2bo5bo$3o3b3o$$$$bo5bo$2bo5bo$3o3b3o!", "width": 9, "height": 9, "cellCount": 20 }

Worth noticing in that output: four consecutive $ characters ($$$$) mark the three blank rows between the two tiled gliders — one $ per row, rather than a compressed 4$. Our encoder is correct, standard RLE (a parser reading $$$$ and 4$ produces an identical result), it just doesn't bother with that particular compression, since these strings are built for exact MCP tool calls, not for minimizing bytes in a shared file.

normalize_pattern — re-base any RLE (yours or a library pattern's) so its top-left live cell sits at (0,0), useful before composing it into a larger scene:

{ "rle": "3o$o$bo!", "width": 3, "height": 3, "cellCount": 5 }

Same five-cell shape, same relative geometry, just re-anchored so its bounding box starts at (0,0) instead of wherever it happened to sit before.

create_composition places multiple patterns (by ID or raw RLE) at exact coordinates with optional rotation, and hands back one combined RLE string for the whole scene. If you've read our MCP server tour, this is the tool that turns "put a glider here and a block there" into a single pasteable pattern instead of a paragraph of coordinate descriptions.

Try it yourself, no code required

You don't need the MCP server to use RLE — it's built into the simulator directly. Click the + button (pattern library) in the toolbar, switch to the Custom RLE tab, and paste any RLE string — one you wrote by hand, one from a tool call above, or one from any other Life pattern archive. The importer accepts the same #N/#C comment lines and x =/y = header shown above, or just the bare cell data ending in !.

Try pasting the gun from the top of this article. Or write your own: a single live cell is just o!. A 2×2 block (the simplest still life) is 2o$2o!. A blinker — three cells in a row that oscillates between horizontal and vertical — is 3o! (a single row is enough; the simulator figures out the rest once it starts running).

Why this format won over anything fancier

RLE isn't clever in the way a general-purpose compression algorithm is clever — it doesn't model entropy or build a dictionary. It works because Life patterns have a specific shape of sparsity: mostly dead cells, in runs, on a grid you're going to scan row by row anyway. A plain list of live-cell coordinates would work too, and for very dense or scattered patterns it sometimes wins — but for the kind of hand-built, structured patterns this whole hobby is made of (guns, spaceships, oscillators, still lifes), runs of b and o compress almost everything for free. That's also exactly why it's a good fit for MCP tool calls: an LLM spends tokens on the string either way, and a 99-character gun beats a 36-line coordinate list by a wide margin.

If you want to see the format in the context of a full, real pattern rather than isolated snippets, our Gosper glider gun walkthrough builds the exact 36-cell pattern from this article cell by cell, and our MCP sandbox tutorial shows an AI agent fetching, running, and predicting patterns using nothing but the RLE this piece just decoded.

Once you've got a string of your own — hand-written, copied from somewhere else, or generated by a tool call — the fastest way to see whether it's right is to paste it straight into the simulator's Custom RLE tab and press play. If you'd rather have an agent generate and manipulate the RLE for you, connect it to our MCP server and give it the same four characters to work with.