Rule 110 is an elementary cellular automaton; a one-dimensional grid of binary cells where each center cell updates based on its 3-cell neighborhood (cells to the left and right of it):
| 111 | 110 | 101 | 100 | 011 | 010 | 001 | 000 | |
|---|---|---|---|---|---|---|---|---|
| output | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
Reading the outputs as a binary number: .
In 2004, Matthew Cook proved the universality of Rule 110; that this is Turing Complete. Given any TM, you can build a Rule 110 initial tape whose evolution faithfully reproduces the computation. Previous universality results for cellular automata worked by designing the automaton’s lookup table to implement a chosen algorithm. Cook’s approach was the opposite: take the simplest automaton that naturally exhibits complex behavior, and find computation already hiding inside it.
I implemented Cook’s full construction as a C++ compiler and simulator. The flagship demo computes entirely through Rule 110: 5 steps of a Turing machine become 338 million cells evolving through nearly 3 billion generations. A 67.7-million-fold blowup.
If you start Rule 110 from a random row of cells and watch what happens, at first the behavior looks chaotic. But as time progresses, periodic structures emerge and move through a background pattern that Cook calls ether.
Gliders emerging from our compiled Rule 110 output. Periodic structures propagate through the ether background at fixed velocities.
Ether is a lattice of white triangles with spatial period 14 and temporal period 7 (shifting 24 cells per generation). Any localized disturbance in the ether propagates as a glider: a persistent, periodic structure that moves at a fixed velocity against the ether background.
All recorded gliders of Rule 110 (from Cook’s Figure 5). Top row: A through . Bottom row: through H and the glider gun. The A gliders can pack closely together (); the other extendable gliders (, , , ) can be any positive width. Gliders sharing a letter have the same slope.
Cook catalogs all recorded gliders: , , , , through , , , , , , , , and a glider gun. Each has a characteristic width (measured mod 14, since that’s the ether’s spatial period) and a period expressible as a combination of and units: the fundamental movement vectors of the A and B gliders. Closely packed gliders are denoted .
| Glider | Width | Period | |
|---|---|---|---|
| 6 | (3, 2) | (1, 0) | |
| 8 | (4, -2) | (0, 1) | |
| 3 | (7, 0) | (1, 1) | |
| 11 | (10, 2) | (2, 1) | |
| 5 | (10, 2) | (2, 1) | |
| 7 | (30, -8) | (2, 6) | |
| 1 | (36, -4) | (4, 6) | |
| (42, -14) | (2, 9) | ||
| 11 | (92, -18) | (8, 17) |
The foundational insight of Cook’s construction is preservation of spacing. Consider what happens when a cluster of gliders (which are stationary; period ) encounters a cluster of gliders (which move to the left - period ). Each collision between a and an produces a new and a new ; effectively, they “cross” each other.
The relative spacings between the s are preserved after crossing, and the same is true for the s. If two s started with a certain gap between them, they still have exactly that gap after an has crossed them both. This means a second will cross the s in exactly the same way as the first.
Preservation of spacing (Cook’s Figure 11). When s cross s, the spacings are preserved - both between the s and between the s. The s end up in the same arrangement they started in.
The same phenomenon in our compiled output. The colored stripes at left are stationary tape data ( clusters from E/F/D blocks). The diagonal traces are crossings implementing the computation.
This solves a fundamental problem in one-dimensional computation: how can two pieces of data cross each other without mutual destruction? By encoding data in the spacings between gliders of the same type, two data streams at different speeds can cross without interference, using just one type of crossing collision.
The second key mechanism is ossification: gliders can convert gliders into gliders. When an collides with an , the outcome depends on their relative position. In the “short reaction,” the is consumed and a is produced in its place. This converts moving data (encoded as spacings) into stationary tape data (encoded as spacings).
The spacing between two incoming s determines the spacing between the resulting s, but the relationship is inverted: wider gaps produce narrower gaps, and vice versa. This inversion is a natural consequence of the geometry.
Crucially, s that are spaced in the right way relative to the s will pass invisibly through the entire ossifier array without being converted. These invisibles traverse both the tape data and the ossifiers with no effect. Since each acceptance or rejection event generates a pair of invisibles, they form a permanent record of the entire computation’s history.
Invisibles vs. ossification (Cook’s Figure 14). s spaced at regular intervals from each other can either pass invisibly through the array or be ossified into s, based solely on their relative spacing. The five s shown have different spacings from each other, producing a mix of crossings and conversions.
Cook’s goal is to show that Rule 110 can simulate any Turing machine. He does this by chaining three reductions, each simplifying the computational model until it maps onto glider interactions.
A tag system operates on a word over a finite alphabet. At each step, the first symbol determines a production string that gets appended to the end, and the first symbols are deleted. Tag systems can simulate Turing machines; the TM’s tape contents are encoded into the exponents of repeated symbols, using a base- positional system. This means each additional tape cell multiplies the word length by .
A cyclic tag system (CTS) simplifies tag systems down to a binary alphabet and a fixed cyclic list of appendants. At each step, the first bit is read and deleted. If it was Y, the current appendant is appended; if N, nothing happens. The appendant pointer advances cyclically regardless. Tag symbols are encoded via one-hot encoding: symbol becomes a block of bits with a single Y at position . This means a tag alphabet of size produces appendants (plus empty padding ones).
The critical observation is that a CTS step maps naturally onto glider collisions: a moving object reads a stationary object, and the result either propagates or doesn’t.
With these mechanisms in hand (crossing, ossification, invisibles) Cook defines an abstract glider system for simulating a cyclic tag system. The cast:
One CTS step works like this: a leader arrives from the right and reads the first tape symbol. If it’s N (reject), the appendant is destroyed. If it’s Y (accept), the appendant is converted to moving data, crosses the tape, hits the ossifiers, and becomes new tape data. Either way, the first tape symbol is consumed. This is exactly what a cyclic tag system does: read and delete the first bit, conditionally append.
A glider system emulating a cyclic tag system (Cook’s Figure 3). Time flows downward. The vertical stripes in the central chaotic region are stationary tape data (Y in black, N in light gray). Leaders arrive from the right as zig-zag patterns. When a leader hits an N, it produces a rejector that wipes out the table data. When it hits a Y, an acceptor converts the table data into moving data that crosses left through the tape, hits the ossifiers on the left, and becomes new tape data.
The pipeline implements these reductions concretely:
Pipeline overview for . Four panels show the TM tape (5 cells), tag system word (10,862 H/L/R symbols), CTS tape (586,548 bits), and R110 regions (338M cells). The blowup at each stage: , , - totaling .
The cascade animation shows the full pipeline blowup: TM tape cells expand into tag symbols, which expand into CTS bits, which expand into the R110 tape.
For a TM with states and tape symbols, Cook’s encoding uses deletion number and an alphabet of symbols organized into seven families:
The , , families encode the TM head, left tape, and right tape respectively. The subscript indexes TM states; indexes tape symbols. The first four families are “state-only” symbols that expand into symbol-specific variants during execution:
The symbol-specific productions encode TM transitions. For :
Halt transitions produce empty strings, eventually shrinking the word below the deletion threshold.
The initial word is encoded exponentially. For a tape with the head on symbol in state :
Tape contents go into the exponents using a base- positional system. For our addition TM (, , so ):
The conversion uses one-hot encoding: each tag symbol from an alphabet of size is encoded as a block of bits with a single 1 at position :
The tag system’s deletion number means symbols are consumed per step, which in the CTS translates to bits read per tag-step cycle. Of these, exactly are Y bits (one per symbol), each triggering the corresponding appendant. The other are N bits with empty appendants.
For : , padded to 54 (Rule 110 block alignment requires multiples of 6). Appendants: 54 real + 324 empty = 378. Initial CTS tape: 586,548 bits. The CTS halts after 46,103,904 steps.
The CTS tape and appendants are spatially encoded into a Rule 110 initial condition using twelve block types (A through L), each a precomputed bit pattern that produces specific glider structures when evolved:
| Block | Role | Glider interpretation |
|---|---|---|
| A | Ether (background) | Pure ether |
| B | Left periodic marker | Clock signal boundary |
| C | Central origin | Origin marker |
| D | CTS tape separator | Boundary between tape symbols |
| E | CTS tape bit = 0 (N) | cluster with wide spacing |
| F | CTS tape bit = 1 (Y) | cluster with narrow spacing |
| G | Tape terminator | End-of-tape marker |
| H-L | Appendant encoding | Components and leaders from the right |
The initial tape has three regions:
Central part: Block C marks the origin. E/F blocks encode CTS tape bits (N/Y), separated by D blocks. This is the stationary tape data from the glider system. G terminates the tape.
Right periodic part: Encodes the CTS appendants as leaders and components. Each non-empty appendant becomes where encodes a Y-bit and encodes an N-bit. Empty appendants become a single L block (“short leaders”). This is the table data that glides in from the right.
Left periodic part: The ossifier array. The ether gap determines the clock rate:
where , count 1-bits and 0-bits across all appendants.
Each block is emitted at a specific phase (0-29) that determines its exact bit pattern. The phase accumulator starts at 18 and advances via where is a block-specific constant.
For , the compiled tape has 338,523,672 cells:
| Region | Content | Size | % |
|---|---|---|---|
| Left periodic | Ossifier array (ether clock) | 97,155,599 | 28.7% |
| Central | CTS tape as E/F/D blocks | 222,019,435 | 65.6% |
| Right periodic | Appendants as H-L blocks | 19,348,638 | 5.7% |
The tape is bit-packed into uint64_t words. Rule 110 reduces to a single bitwise operation:
uint64_t new_word = (center ^ right) | (~left & right); With 4x loop unrolling, the 338M-cell tape (~5.3M words, ~42 MB) processes one generation in ~38ms. The first 4,800 generations are computed this way to produce the head spacetime image.
Direct simulation of 2.9 billion generations would take ~3.5 years. I adapted Bill Gosper’s HashLife algorithm for 1D with ring topology.
The key data structure is a canonical binary tree where each node represents cells:
struct Node {
Node* child[2]; // left/right halves
Node* result; // memoized step result
int level; // 0=leaf, k means 2^k cells
}; Every (left, right) pair maps to a unique Node* via a hash table, so identical subtrees share the same node. Since ether dominates the tape (~96% of cells), the tree is extremely sparse. step(node) computes the central cells after generations. Because nodes are canonical, identical configurations are never recomputed.
step_by(node, n) computes the result after exactly generations by recursively decomposing:
where and .
The HashLife tree grows to ~40M nodes and advances through 2.9B generations in ~2,262 seconds - an 18,000x speedup over direct simulation.
The computation is done when the tape has settled into pure ether. Every 30 generations, I compare the tape at generation with the tape at generation , offset by cells to account for ether drift:
Ether shifts 24 cells per generation. After 7 generations ( temporal period), it shifts cells — exactly 12 spatial periods — so the pattern returns to its original appearance. The 30-generation sampling interval is a convenient multiple that aligns with the block phase system. When the mismatch drops below 300, the tape has settled.
To track computation progress, we measure the active width: the region of the tape that hasn’t yet settled into ether. This is computed by scanning inward from both edges of the central region, looking for the first period-14 break:
// From left: find where ether ends
for (size_t i = start + 14; i < end; i++)
if (cell(i) != cell(i - 14)) { left_ether = i - start; break; }
// From right: find where ether ends
for (size_t i = end - 15; i > start; i--)
if (cell(i) != cell(i + 14)) { right_ether = end - 1 - i; break; }
active_width = total_width - left_ether - right_ether; The active width starts at the central region size and narrows as ether fills in from both sides, collapsing to zero at settling. For , this means ~222M active cells narrowing over nearly 3 billion generations.
The binary addition TM encodes two numbers by interleaving their bits: , - complementary bit patterns. The tape symbol is , giving symbols in . The machine scans right, propagating carries.
TM execution trace for . Each row shows the tape at one step. The machine scans right and halts after 5 steps.
Initial tape: . Final tape: . Decoding: . So .
After compilation through the full pipeline:
| Metric | Value |
|---|---|
| Tag system alphabet ($ | \Phi |
| Tag word length | 10,862 |
| CTS appendants | 378 |
| CTS tape length | 586,548 bits |
| CTS steps to halt | 46,103,904 |
| R110 total cells | 338,523,672 |
| Settling generation | 2,918,400,960 |
| Compile time | 1,308 ms |
| Simulation time | 2,286 s |
| Total blowup |
Five steps of a Turing machine, faithfully simulated through nearly 3 billion generations across 338 million cells.
Head spacetime (generations 0-4,800). Left and right views of the full 250,000-cell viewport (downsampled). The gap between panels indicates the 338M total cells. Left: left periodic ether (ossifier array, gray). Right: central tape data (colored E/F/D stripes - blue for 0-bits, green for 1-bits, orange separators) meeting the right periodic appendants (pink/red).
Right view annotated. Leaders arrive as diagonal zig-zag patterns from the right, hitting stationary tape data (vertical colored stripes). Accept events produce leftward-moving traces that cross the tape; reject events produce rightward rejectors that destroy the incoming table data. Magenta regions are glider collisions.
Tail spacetime showing the final moments before settling. The active computation has narrowed to a thin strip, with ether filling in from both sides as the active width collapses to zero.
The full evolution from generation 0 to settling at ~2.9B, compressed with logarithmic Y axis:
Mismatch count (blue) and active width (orange) over time. Mismatch initially rises as glider interactions spread, then collapses as the computation completes. Active width narrows monotonically as ether fills in from both edges of the central region.
Tag system animation. The bar is colored by symbol family - orange (H/head), blue (L/left tape), green (R/right tape). The red bracket marks the deletion region (first symbols). Width scales with word length.
CTS animation. The green bar shrinks as bits are consumed over 46M steps.
Progressive top-to-bottom reveal of the Rule 110 spacetime diagram, cropped to the data region.
Correctness is verified at five levels, from individual pipeline stages through end-to-end round-trip decoding:
Checks:
TM halts <=> CTS halts: PASS
R110 settled: PASS
10 + 21 = 31 (computed through Rule 110) All seven test cases pass all five verification levels.
The same pipeline runs on simpler TMs. Here are three base cases on 2-symbol machines.
A 1-state TM that halts immediately: 0 computation steps, but still produces a 2.5M-cell R110 tape that settles in 1.7M generations. The entire overhead comes from the encoding, not the computation.
Even with zero computation, the tape produces glider traces. The left periodic ether dominates since the central region encodes only a single tape cell.
A 3-state TM that increments a binary number (5 steps). 30.8M cells settling in 20M generations. The most complex 2-symbol test case, with the largest transition table.
Head spacetime for binary increment. The right panel shows denser glider interactions than simpler test cases, reflecting the 3-state TM’s larger transition table and 86K CTS steps.
A 2-state TM that appends a tally mark (2 steps). 13.2M cells.
Head spacetime for unary increment. Sparser glider traces than binary increment — fewer CTS steps (15K vs 86K) means fewer leader/appendant interactions.
The same addition TM but with a 3-cell tape. 121M cells settling in 89M generations. Same TM, different input size - useful for seeing how the exponential encoding scales.
Compare the region proportions with : here the left periodic ether dominates (80.3%) because the central region is much smaller (11.8K CTS bits vs 586K).
The gens/CTS step ratio varies dramatically:
| Test | CTS Steps | Settling Gen | Gens/CTS Step |
|---|---|---|---|
| Immediate halt | 288 | 1,743,330 | 6,053.2 |
| Left-then-halt | 2,880 | 8,001,390 | 2,778.3 |
| Unary increment | 15,264 | 10,875,420 | 712.5 |
| Binary increment | 86,592 | 20,060,730 | 231.7 |
| Addition () | 861,840 | 89,419,080 | 103.8 |
| Addition () | 46,103,904 | 2,918,400,960 | 63.3 |
Trivial computations pay high overhead; as complexity grows, the ratio approaches a more efficient regime. The tape size grows as — each additional tape cell multiplies everything by . Two extra tape cells from to multiply the tag word by , cascading through every stage.
This construction is spectacularly impractical for actual computation. That was never the point.
TuringMachine (concrete state machine)
|
v [TMToTag — Cook's symbol algebra]
TagSystem (word + deletion number + production rules)
|
v [TagToCyclic — one-hot encoding, alphabet padding to mod 6]
CyclicTagSystem (binary tape + cyclic appendants)
|
v [Rule110Compiler — block emission with phase-tracked alignment]
Rule110State (three-region bit vector + block maps)
|
v [Rule110Runner — 64-bit parallel simulation]
v [HashLife1D — quadtree memoization for exponential speedup]
|
v [BlockDetector — visualization]
v [Decoder — round-trip verification R110 → CTS → Tag → TM] Each layer is independently testable. The 12 block types (A-L) are precomputed bit patterns stored in BlockData.hpp (497 lines, ~147 KB). Each block has a pattern for each of 30 phases. Block A (ether) is 28 bits wide and identical across all phases. Other blocks range from 22 to 350 bits depending on phase.
The BlockDetector tracks clusters across generations by matching period-14 breaks (non-ether regions). Each cluster inherits its block type from the previous generation via nearest-center matching (within 50 cells). Collisions between glider structures are detected and colored magenta.
The simulator runs in three phases: direct simulation for the first 4,800 generations (producing the head spacetime image), HashLife with exponentially growing step sizes to find the settling point, then a rolling-buffer direct simulation for the tail image. During HashLife, ~1000 spacetime crops are sampled at logarithmically-spaced generations for the full-evolution diagram.
The hardest parts were HashLife and phase tracking. HashLife on a ring topology with partial stepping required careful verification: the memoization makes it difficult to debug when results are wrong, since the error could originate from any cached subtree. I validated by comparing HashLife results against direct simulation for the first few thousand generations. Phase tracking (ensuring each block is emitted at the correct phase offset so glider structures align properly) was similarly tedious; a single off-by-one in the phase accumulator produces a tape that looks almost right but fails to compute.
CMake 3.10+, C++17. No external C++ dependencies.
This project mechanizes Cook’s universality proof for Rule 110. Given any Turing machine, the compiler produces a Rule 110 tape, the simulator evolves it to completion, and round-trip decoding confirms the result.