This TV runs on texture nodes
I built a working computer out of Substance Designer graphs and gave it a game. Every brick you see break was decided by a Pixel Processor executing the game's own machine code. Nobody animated that screen.
The machine
CHIP-8 is a tiny virtual computer from the late 70s, first run on the COSMAC VIP. It has 4 KB of memory, sixteen 8-bit registers, a stack, two timers, a hex keypad and a 64×32 screen with one bit per pixel. Programs are raw machine code: 35 kinds of two-byte instruction.
The game is Br8kout, a Breakout clone SharpenedSpoon wrote in 2014 and released CC0. I didn't port it or redraw it. The graph reads the ROM's bytes out of a bitmap and executes them, seven instructions every 60th of a second, while a recorded keypad tape plays the paddle.
Below is the same program, same tape and same rules, running in your browser on an interpreter written to the graph's spec. The strip under the screen is the part I still find hard to believe. That's the whole machine: registers, stack, timers and all 2,048 screen pixels, packed into 48 pixels of a texture. In the film's build, it's exactly what one Pixel Processor hands to the next.
- Frame
- 0
- Replayed from power-on
- 0
- PC
- 200
- I
- 000
- Delay timer
- 0
- Clock
- 60 Hz · 7 per frame
One byte per pixel. Font at the top, the game from byte 512. Amber is the program counter, green is I. Hover to read a byte.
Four things a Pixel Processor doesn't have
A Pixel Processor exists to shade textures. It runs one small function for every output pixel and forgets everything afterwards. You get arithmetic, comparisons, if/else, a while loop, variables and image samplers. That's the whole toolbox, and a computer needs four things it doesn't include.
01Memory
There's no writable memory. So the 4 KB of RAM is a 64×64 grayscale image, one byte per pixel, read with a sampler. You saw it in figure 1. Anything the program changes lives in graph variables instead. Getting RAM writes around that came later, and chapter 6 is how.
02Arrays
There are no arrays. When an instruction says "add to register X" and X is only known while the program runs, the graph has to pick the right variable out of sixteen. Every write is a tree of if/else nodes with a Set at each leaf.
It only works because Designer runs nothing but the branch an if/else takes. Writing one of 16 registers costs four comparisons and a single Set. Writing one of the 144 words that make up the machine costs about seven comparisons. The framebuffer depends on that one engine behavior.
03Bits
CHIP-8 is built on AND, OR, XOR and shifts. Designer has no XOR and no bit shift, and its And and Or work on true and false, not on bytes. So every byte gets split into its eight bits with floor and modulo, combined bit by bit, and put back together.
That broke once in a way I didn't expect. On the CPU engine, pow(2, n) comes back a hair under the whole number. One framebuffer word came out as 49,151 instead of 49,152, and a single pixel went wrong. Now every power of two in the graph gets rounded.
04Time
Nothing survives between frames. Each frame is cooked from scratch, so each one boots the computer from power-on and replays the game up to that moment, keypad tape included. Frame 60 is 420 instructions. Frame 5,700 is 39,900.
The crash
My first version ran the whole CPU inside one Pixel Processor. Up to 36,000 instructions it was perfect. At 65,000 the GPU sat on a single job too long, macOS revoked Designer's access to it, and every cook after that came back empty until I restarted. Once the hang outlived the process and took the whole session down with it.
So the CPU is cut into five Pixel Processors. Each one is its own GPU job, runs at most 8,000 instructions, and writes the entire machine into a 64×1 float texture. In the film's build that's 144 numbers, three to a pixel. The next node reads that texture and picks up exactly where the last one stopped.
A Pixel Processor runs once per output pixel, so every segment actually runs the full computer 64 times side by side, and each copy keeps only its own three numbers. Across the 95 second run that's about 114 million CHIP-8 instructions, times 64.



Proof
An emulator that's almost right is wrong. One bad collision bit and the ball goes somewhere else a few seconds later. So a separate interpreter, written to the same spec and checked against the Timendus CHIP-8 test suite, plays the same ROM with the same tape, and every frame Designer rendered was diffed against it. All 5,701 frames of the 95 second run match.
The Br8kout+ frames in this figure came out of the 29 KB .sbsar on a different machine, a Windows PC, through sbsrender. The Dino Run frames are from the new build. Your browser recomputes the reference and diffs them live. Frame 5,700 took 1.3 seconds, including startup.
The ending Br8kout never had
I gave the computer a player that can't miss and found out Br8kout has no ending. I disassembled all 199 bytes. There's no brick counter anywhere, so when the last brick goes the ball just bounces around an empty field forever.
That felt like a letdown, so I wrote the ending in the game's own machine language: 66 bytes appended to the ROM and four of the original instructions rewired. It counts bricks in a register the game never touches. At 49 it freezes the ball for a second, clears the screen, puts up the next level number from the computer's built-in font, and starts a fresh wall one step faster. It only uses instructions the Designer CPU already had.
A second game
A machine that only runs Br8kout is just a very elaborate Br8kout. So I pointed the memory bitmap at a different program: Dino Run, a CC0 endless runner by Andrezinrc. On the untouched 37,190-node graph, at seven instructions per frame, 2,992 frames came out, 49.9 seconds of game, every one bit-exact.
A real computer also has to write to its memory, and the RAM here is an image, which a Pixel Processor can't write to. So the bitmap stays read-only and writes go somewhere else: an overlay carried in the machine state, handed from segment to segment with everything else. Reads look in the overlay first and fall through to the bitmap. That's how FX33, FX55 and FX65 got in, the instructions that store bytes to RAM and load them back.
The overlay has a limit. The state texture has room for 47 slots, and a test ROM proved all of them for 172 frames, covering BCD, sprites drawn from RAM and self-modifying code. But the full overlay about halves the GPU budget even when it's empty, so the slot count is a per-ROM build setting and the shipped graphs use 4. A ROM that writes to more than four different addresses would lose those writes. Verification would catch it, but the game would be wrong. Dino Run writes three addresses. Br8kout+ writes none.
Dino Run also got its author's clock back, 15 instructions per frame instead of 7. That's 900 instructions a second, frame 5,700 replays 85,500 of them, and the graph grew to 14 segments of 6,500 instructions each: 124,062 function nodes in a 35.4 KB .sbsar. On the new build, Dino Run and Br8kout+ each pass 5,701 of 5,701 frames bit-exact. The Br8kout film at the top of this page still comes from the old graph.
Then it went back on the TV. This is one take with no cuts, 13.4 seconds, the camera held by hand in the same den. On the screen are game frames 2,879 to 3,683, between the level-ups at scores 20 and 30 that flip the screen.
Watch the score
About a second before that clip ends, the score reaches 29 and the 9 comes out wrong. That isn't a render fault. Dino Run runs F055 with I pointing at the font, right at the glyph for 9 at address 0x2D, and from frame 1,803 on that overwrites the top row of the digit. Of Dino's three write addresses, this is the only one where a byte actually changes. The other two write 255 over 255.
The graph reproduces it exactly, because the graph is running the machine code, whatever that code does. My read is that the ROM's author didn't mean it, but that's an inference, and I haven't run it on another emulator. I kept it. It's a corrupted digit on a TV, produced by a texture graph writing a byte into its own font. Set figure 1 to Dino Run and drag to frame 3,650 to see it, with the whole 5,701-frame run on the same slider.
The tube
The CRT is a second graph. It takes the current frame and the four before it and keeps the brightest, each faded by 0.62 per frame. That's why the ball leaves a short trail like real phosphor. Then it draws soft round beam spots, a glow, two scanlines per row, an RGB mask and the curve of the glass.
The game is 2:1 and the TV glass is 4:3, so every game pixel lands 1.51 times taller than it is wide. The graph squashes its dots ahead of time so they come out round.
Figure 7 rebuilds that recipe in your browser so you can pull it apart. The video under it is the real graph's output, straight from sbsrender.

The room
Then the picture needed somewhere to live. The room is Blender with CC0 assets from Poly Haven: a 70s TV with the maker's logo painted out and a SUBSTANCE-8 badge in its place, a wooden table, a lounge chair, a plant and paneled walls. The CRT frames map onto the curved glass as emission, and a hidden green light over the glass lets the game light the room.
Here's one frame, end to end, through every program that touched it.
Every light in the room rendered into its own pass. Once that's done, light is addition: residual plus each light times its gain, plus the lens effects. Those passes are real, from the final render. Move the sliders and you're relighting frame 1,300 yourself.
The finish happened in After Effects: an AgX view, a light grade, fine grain and a vignette, plus a power-on and power-off built from the screen's own light pass, so the room really does go dark when the TV does.
The last 25 seconds of the cut were one slow shot of a nearly empty board, so I re-timed it instead of cutting away: three times speed through the dead rally, real time for the last two seconds, half speed on the final brick. The dots below are game events I tracked out of the screen frames. The same events trigger every blip in the soundtrack, which is synthesized from scratch.
Numbers
The film's build ran 31 of the 35 CHIP-8 instructions, everything Br8kout needs. The current build runs 35 of 35 if you count 0NNN as ignored, 34 if you don't. Programs can write to RAM, up to four addresses in the shipped graphs.
Dino Run by Andrezinrc (CC0, via chip8Archive).
TV, furniture and HDRI from Poly Haven (CC0).
Built in Substance 3D Designer. Staged in Blender. Finished in After Effects.