Say a blank background cell sees a ball cell to its left. It looks at the precise color of the ball to determine that the ball's moving to the right, so the next cycle, the background cell must become a ball cell with the same motion. It's a bit more complicated than that because balls only move every other cycle, they've got internal counters [colors] to deal with moving diagonally, bouncing off walls, hitting the paddle, etc., but that's basically it.
The actual state machine code takes thousands of lines of JavaScript to express.
In fact, @imode, if you want to look at one of the smaller state machines, look at the scoreboard code in https://github.com/ericu/CellCulTuring/blob/master/scoreboar.... It's a seven-segment display with some tweaks and a hack to show the game-over message. I initialize the scoreboards by drawing the segments in hard-to-see colors so that they're invisible when they're turned off. Each cell of the scoreboard always knows the current score; they propagate the updates to each other as fast as the find out about them. Each segment has an ID [a different color field] and a lookup table that tells for which scores e.g. ID 3 should be visible. So then when each segment is supposed to be visible, it sets a high-order color bit to show up. That's one of the few places that I actually have to set bits just for visibility. For the ball, wall, paddle, and such I just chose the bits such that you could see what the cells were naturally--I didn't have any bits to spare. For the sub-space of bits allocated to the scoreboard, I've got plenty extra. I was considering doing a fancier font using a 1-hot encoding instead of 7-segment, but I wanted to get this done.
(There was a similar thing posted here a while back, too, that used modified rules to make it a bit quicker and simpler, but I can't remember what it was called...)
If you wanted to support doubles play, it might be possible with enough tweaks to the rules, but it would not be as simple as just drawing more paddles.