← Cellular Automata From First Principles

Hidden Cell Channels and Local Memory

A visible pixel is not enough state for a cell that must also coordinate growth.

So give every cell a vector.

[R, G, B, alpha, h1, h2, ... h12]

The first channels can be rendered. The rest are private internal state. (Chapter 37 deferred hidden-channel ownership here; this chapter takes it.)

CHANNELS = 16
VISIBLE = 4

state = torch.zeros(1, CHANNELS, 64, 64, device=DEVICE)

The hidden channels have no labels. We do not tell the model that channel 8 means “distance from the center” or channel 11 means “grow east”. If useful internal signals exist, training must discover them.

A seed becomes a full cell state

def make_seed(size=64, channels=16):
    x = torch.zeros(1, channels, size, size, device=DEVICE)
    c = size // 2
    x[:, 3:, c, c] = 1.0
    return x

Only one location starts alive — RGB dark, alpha and hidden set — matching the canonical seed convention. But it already contains several internal values.

Visible state is only a projection

def rgba(x):
    return x[:, :4]

This is a useful mental model:

full cellular state
        ↓ projection
visible organism

The thing we see is not the whole dynamical system.

Local perception across every channel

The same identity and gradient filters can be applied independently to all channels. A 16-channel state with three perception filters becomes 48 local features per cell.

perceived = perceive(state)
print(perceived.shape)
# [batch, 48, height, width]

Those features then enter the shared 1×1 neural rule.

Hidden state can carry information across updates

A cell can change a hidden channel now and read it many updates later. Nearby cells can sense gradients in that channel. Information can therefore propagate without appearing directly in the rendered image. That is memory only in the thin sense of state that persists between updates; what, if anything, a trained rule stores there is a separate question.

Possible learned uses include:

local phase
boundary signal
growth readiness
orientation cue
repair signal
internal timer

Those are hypotheses, not guaranteed interpretations. Visible and hidden channels play strictly separated roles in the update — same rule, different contracts:

    flowchart LR
    V[visible RGBA] --> U[shared local update]
    H[hidden channels] --> U
    U --> V2[visible output]
    U --> H2[hidden next state]
    H2 --> H
  
Channel kindSupervised by loss?Directly observable?Legitimate roleNever infer
visible (RGBA)yes, MSE to targetyes, renderedoutput + recurrence inputthat rendering exhausts its role
hidden (12)no, only via rolloutno, probe onlylatent coordination signalssymbolic meaning, memory-as-mind

Whether hidden state causally matters — rather than merely accompanying success — is testable. Zero all hidden channels mid-rollout on a trained model and compare: if the pattern recovers its trajectory, hidden state was not needed at that moment; if behavior collapses or reroutes, the rule depends on something carried in it. Even then, “depends on” is weaker than “memory”. (Verified: the intervention executes cleanly with shapes preserved. Run it against your trained weights and let the outcome decide how much interpretation is earned.)

Living-cell masks

Growing NCA systems often distinguish cells that belong to the organism from empty space using an alpha-like channel.

def living_mask(x):
    alpha = x[:, 3:4]
    return F.max_pool2d(alpha, kernel_size=3, stride=1, padding=1) > 0.1

(Verified: a single seed yields exactly 9 living cells — the 3×3 neighborhood the canonical rule specifies.)

After an update we can remove state from cells that are not near living cells:

def apply_life_mask(before, after):
    pre = living_mask(before)
    post = living_mask(after)
    return after * (pre & post)

That prevents arbitrary hidden activity from spreading infinitely through empty space. Fidelity note: the canonical form masks on pre-update aliveness alone; this variant additionally requires post-update support, making it stricter against stray growth — a documented pedagogical choice, not the paper’s formula.

The cell is now a tiny recurrent machine

Each cell has:

private state
local perception
shared transition function

That is already enough for surprisingly rich distributed computation.

The next question is whether this system can coordinate itself from one seed into a prescribed global structure.

In the next chapter we train it to grow a target image.


Research

  • Mordvintsev, A., Randazzo, E., Niklasson, E. & Levin, M. — Growing Neural Cellular Automata (Distill, 2020). The 16-channel specification owned here: RGB visible, alpha alive-threshold at 0.1 with 3×3 neighborhood masking, twelve hidden channels explicitly without predefined meaning (“up to the update rule to decide”), seed convention, and the chemical-signaling interpretation offered as analogy. The ablation experiment above is how to test whether that analogy earns its keep. https://doi.org/10.23915/distill.00023