An explainer you click through Every number comes from our own runs on one GPU.
How does a neural network become Flappy Bird?
There is no game engine here. A model looks at the last 15 frames and the key you press, and it draws the next frame. We built that model from zero, trained it in three stages, and checked it by letting it play.
Simple English ASD-STE100 rules: short sentences, one word for one ideaA diagram for each idea one question per page, one drawing answers itYou step through it press → or click a step on the right; the drawing builds one step at a time
97%new pipes appear after its own frames (stage 3)
Chapter A1 The map. Every box on this page gets its own page later.
What happens between your tap and the next frame?
Real pictures from our stage 3 model: one real game frame (dataset frame 17,827), no tap.
A memory of 15 frames
The model keeps the last 15 frames, each with the key that made it. At the start they are real frames. After 15 frames, every frame in memory is one the model drew itself.
Your key
You press TAP or nothing. That one bit goes in with the new frame. It is the only thing you control.
Start from pure noise
The new frame starts as random static. The transformer must turn this static into the next frame.
Two passes of one transformer
Each pass reads the noise, the 15 frames in memory and your key. It outputs a slope for every pixel, and we move the static along it. Two moves give a clean frame.
172.5Mparameters, 16 blocks
2passes to draw a frame in play
Show it, then remember it
The frame goes to your screen. A third pass stores it in memory, and the oldest frame drops out. Then the loop starts again.
3passes per frame: 2 draw, 1 stores
The transformer draws every frame of the game. It draws the pipes, the bird and the score digits as pixels. A small pixel check outside the model decides game over. Idea from: World Models (Ha & Schmidhuber, 2018); GameNGen (Valevski et al., 2024); DIAMOND (Alonso et al., 2024).
Chapter A2 A1 showed the whole loop. Now: what exactly goes in?
What does a game world model do, and where is the game engine?
15 real frames go in
Each small picture is one real frame from a real game. The number above it is its frame number. The model reads all 15 frames.
15frames in
1frame out
Every frame has its own key
The key is 0 for no tap and 1 for a tap. The key under a frame is the key that made that frame. Past keys are not dropped.
You choose the next key
Use the two buttons above the picture. The 15 frames stay the same. Only your key changes. It is the key that will make the next frame.
The model draws the next frame
The model draws the right-hand big picture itself, from the same 15 frames and the same noise. Press each button and watch the bird. With no tap it falls. With a tap it rises.
58.0bird height now
60.4bird height drawn
The new frame joins the memory
The oldest frame leaves. Memory holds 15 frames again, so the loop repeats. No game engine runs in this loop. The model reads pixels and writes pixels.
What do we call this model?
It is an action-conditioned, autoregressive video diffusion transformer, trained with rectified flow on raw pixels. The next pages open each word.
The world model is a frame-and-key predictor. Frames and keys go in, one frame comes out, and the loop never calls a game engine. Idea from: World Models (Ha & Schmidhuber, 2018); GameNGen (Valevski et al., 2024); DIAMOND (Alonso et al., 2024).
Chapter A3 A1 and A2 showed the loop. Now: where did the model see Flappy Bird?
What did the model learn from?
Counts from the dataset files and the prep step. Bar lengths are frames.
300,153 frames, 171 games
The data is a public Flappy Bird set (Dreamer4 format). Each frame is a 128 x 128 colour picture. Each frame also has the key the player pressed.
Three kinds of player
28 games never tap, 28 games always tap, and 115 games choose when to tap. The choosing games hold 98.8% of all frames. The longest game has 30,000 frames.
Split by game, not by frame
14 whole games are held out: the model never trains on them. Neighbour frames are near copies, so a split by frame would leak the test into training.
21,859held-out frames (7.3%)
A crash only ends a game
168 games end in a crash, on their last frame. 3 more games stop at a 30,000-frame time limit. So the data never shows what happens after the bird hits a pipe.
All of it sits on the GPU
During training, all 300,153 frames stay on the GPU as bytes: 14.75 GB. A batch is cut out of that block with no disk reads.
One game's pictures plus one bit per frame. That is all the model ever sees. Idea from: Dreamer 4 (Hafner et al., 2025), the Flappy Bird data format: a frame and a key per frame.
Chapter A4 A3 gave frames and keys. Now: how do they pair up?
Which key goes with which frame, and what is one training example?
One game, drawn as a strip of frames. Yellow = a tap. Arrows show which frame a key changes. The next page does the same with real frames.
A key changes the NEXT frame
The key pressed at frame t shows up in frame t + 1. So we store, for each frame, the key that produced it: the key of the frame before.
The first frame of a game
Nothing produced the first frame of a game. Its key is set to 0, so no key ever crosses from one game into the next.
A window = 16 frames in a row
One training example is 16 frames in a row from one game, with their 16 keys. A window never crosses into another game.
275,939training windows
21,649held-out windows
Every frame is a target
The model predicts all 16 frames of a window, each from the frames before it. Frame 0 has no earlier frame to read: it sees only itself and its key. Chapter F shows how one pass does all 16.
One step = 8 windows
A training step takes 8 random windows. That is 8 x 16 = 128 next-frame predictions in each step.
128frames predicted per step
The model learns cause and effect from the order of frames. The key into frame k is the key pressed at frame k − 1. Idea from: Dreamer 4 (Hafner et al., 2025), frames and keys in the same order.
Chapter A5 A4 drew boxes. Here is the same idea on real frames.
Which key made which frame, on real frames?
Frames, and the key at each frame
This is a short stretch of one real game, frame by frame. The row under it shows the key pressed at each frame. A 1 is a tap. Most frames show 0.
The key shows up one frame later
The key pressed at frame 73 makes frame 74. So the key that made frame k is the key pressed at frame k minus 1. The second row is the first row moved right by one.
A new game starts with key 0
The first frame of a game has no frame before it. It gets key 0. Games 3 and 4 tap all the time, yet frame 0 of game 4 still gets 0. Without this rule, the last key of one game would leak into the next game.
A window is 16 frames in a row
A window starts at any frame where 16 frames in a row stay in one game. This game has 267 frames, so it gives 252 windows. Windows overlap, and each one is a different example.
A batch is 8 windows
One training step takes 8 windows. That is 8 times 16 = 128 frames to learn from. A pixel byte b becomes b / 127.5 minus 1, so every value lies in the range -1 to 1.
128frames per step
8x16windows x frames
One example is 16 frames and their 16 keys. Each key is the key that made its frame. A batch of 8 examples is the tensor x with shape [8, 16, 3, 128, 128]. Idea from: Dreamer 4 (Hafner et al., 2025), the Flappy Bird data format: a frame and a key per frame.
Chapter B1 A4 gave windows of 16 frames. Now: what does the transformer read from one frame?
How is a frame cut into patches?
hover a pixel in the zoom
Real game frame (dataset frame 17,827). Every number here is read from that frame.
A frame is 16,384 pixels
The frame is 128 pixels wide and 128 high. Each pixel holds 3 numbers from 0 to 255: red, green and blue. That is 49,152 numbers.
Cut it every 8 pixels
Lines every 8 pixels cut the frame into a 16 x 16 grid. Each square is a patch. The patches are numbered 0 to 255, row by row, like reading. That is 256 patches, not 255.
Most patches repeat
Colour shows what each patch holds. Sky, ground and grass repeat across the screen. Only 128 different patches exist. The other 128 are repeats of them.
Zoom in on one patch
Patch 132 (row 8, column 4) holds the bird. Enlarged, it is just 8 x 8 coloured squares. Hover a square to read its 3 numbers.
Read it like text
The 64 pixels are read row by row. The top row is 0 to 7, the next is 8 to 15, and so on to 63. Every patch uses this order.
The model never sees the frame as one picture. It sees 256 small squares and a number that says which square is which. Idea from: ViT (Dosovitskiy et al., 2020), cutting a picture into patches.
Chapter B2 B1 cut out the bird's patch. Now: how does it become a list of numbers?
How does a patch become 192 numbers?
hover the strip
The bird's patch from B1. Hover any cell of the long strip to see which pixel and colour it came from.
Split the colours
The patch splits into three 8 x 8 grids: one of red values, one of green, one of blue. Each cell is one number from 0 to 255.
Lay them end to end
The red grid is read row by row: 64 numbers. Then all 64 green, then all 64 blue. Together that is one list of 192 numbers.
Squash to −1 … 1
Each number b becomes b / 127.5 − 1. So 0 becomes −1, 255 becomes 1, and the middle becomes 0. Networks learn best with numbers near 0.
250 → 0.96the bird's orange, red channel
78 → −0.39sky, red channel
The list, coloured
The strip shows the 192 values: red for positive, blue for negative. The bird's bright orange is the red block on the left.
Nothing is lost. The 192 numbers hold the patch exactly. The next pages turn them into a token. Idea from: ViT (Dosovitskiy et al., 2020), each patch flattened into a vector.
Chapter B3 B1 and B2 cut one patch by hand. Now: how does the code do all of them?
How does the code turn a frame into 256 rows of 192 numbers?
Three reshapes, every square at once
The code does for all 256 squares of all frames what B1 and B2 did for one patch. It needs three lines. Each box shows the shape after one line.
No number changes its value
The view line gives names to the axes: 128 rows become 16 square-rows of 8, and the same for columns. The permute line moves "which square" to the front. The reshape line flattens each square into 192 numbers.
The result is a table
The table has 256 rows and 192 columns. Row 132 is the bird patch from B2. It holds the same 192 numbers: 64 red, 64 green, 64 blue.
256rows per frame
192columns
In training, N is 128
A batch has 8 windows of 16 frames, so N = 128 frames. The same three lines run on all of them. Each copy of the batch becomes [128, 256, 192].
A frame is 256 rows of 192 numbers. This step learns nothing and loses nothing. It only reorders the numbers so each square sits together. Idea from: ViT (Dosovitskiy et al., 2020), cutting a picture into squares.
Chapter B4 B2 gave 192 numbers per patch, B3 did it for all patches. Now: what exactly does the first layer compute?
How do 192 numbers become a token?
hover the strips
Every number is a real weight or value of our stage 3 model, for the bird's patch (patch 132).
192 inputs meet 192 weights
Top strip: the bird patch's 192 numbers from B2. Middle: 192 learned weights. Bottom: each input times its own weight.
Add the products: one number
The line adds the products from left to right. It ends at 0.147. One more learned number, the bias 0.054, makes 0.201. That is output number 0.
Do it 768 times
The layer holds 768 rows of 192 weights: a table of 768 x 192. Each row gives one output number, the same way. So one patch gives 768 numbers.
In PyTorch: nn.Linear(192, 768)
The layer is made with one line: 192 numbers in, 768 out. It is Linear(192, 768), not (768, 192). PyTorch stores the weights as [out, in], so the table is 768 rows of 192. The bias holds 768 numbers.
Keep all 768: that is the token
Nothing is picked or thrown away. All 768 outputs, side by side, are the token of this patch. A token is one row of 768 numbers. Every patch uses the same table.
Why 768?
768 is the model's width, a size we chose when we built it. Every token in every block has 768 numbers. GPT-2 small and ViT-Base use the same width.
There is no codebook and no search for a nearest word. A VQ-VAE would keep 1 integer per patch. This layer keeps 768 numbers, so nothing is rounded away.
Learned with everything else
The table starts as random numbers, spread evenly between −0.072 and +0.072 (PyTorch default). The flow loss trains it with all 16 blocks. After training, row 0 runs from −0.084 to +0.080.
Similar patches, similar tokens
The grid compares the chosen patch's token with all 256 tokens of the frame. Red = alike. Sky matches sky, ground matches ground. Try the buttons.
Same pixels, same token. Identical sky patches anywhere on screen get the same token. The next page fixes that. Idea from: ViT (Dosovitskiy et al., 2020), a Linear layer on each patch.
Chapter B5 B4 gave one token for one patch. Now: all 256 squares, and where each one sits.
How does a row learn where its square sits?
The same layer for every square
The same weights run on all 256 squares. A table of 256 rows by 192 becomes 256 rows by 768. One row is now one token.
768numbers per row
256rows per frame
Same pixels, same row: a problem
Squares 0 and 143 are both plain sky. Their pixels are identical, so the Linear gives identical rows. Nothing in a row says where its square sits.
Add a learned row for each place
The table pos_s has 256 rows of 768. Row i is added to square i. Now the two sky rows differ. This is the only place that says where in the picture.
Nobody tells it rows and columns
pos_s starts as small random numbers and is trained like any weight. Paint one number of each row on the 16 by 16 grid. Some numbers grow from top to bottom. Others grow from left to right.
38numbers follow the row
18numbers follow the column
Each row now says what a square shows and where it sits. The shape of one frame is [256, 768]. With a batch of B windows of F frames it is [B, F, 256, 768]. This row says nothing about time. RoPE adds that later, on page C3. Idea from: ViT (Dosovitskiy et al., 2020), Linear layer and position row.
Chapter B6 B5 turned pixels into rows. Now each frame gets two more facts.
How do a frame's key and its noise level become one row called c?
Every frame has a key
At play time the model holds 15 memory frames and draws one new frame. Each memory frame has the key that made it. Look at the real keys under the strip. Past keys are kept, not only the latest one.
Every frame has a noise level
The noise level t says how much static a frame has. Memory frames have t = 0.05, a pinch of static. The new frame starts as pure static, t = 1. The model must be told how much static each frame has.
So every frame gets its own c
Each frame gets one row of 768 numbers, called c. It is made from that frame's key and that frame's t. In play, one frame goes through per pass, so c is [B, 1, 1, 768]. In training, 16 clean and 16 noisy frames go in together, so c is [B, 32, 1, 768].
The key becomes a row by lookup
The table nn.Embedding(2, 768) has 2 rows of 768 learned numbers. Key 0 reads row 0. Key 1 reads row 1. That is all the key does here.
The noise level becomes a row by a small network
The value t is a decimal, so a table cannot list it. A Linear layer on t alone would only slide along a straight line. So the code spreads t into 256 waves with no weights. Then Linear(256, 768), SiLU and Linear(768, 768) give 768 numbers.
Add them, then SiLU: that is c
Add the key row and the noise row. Apply SiLU, which squashes large negative numbers to about 0. The result is c, one row of 768 numbers for this frame. The code is world.py:58-61.
768numbers in c
1row per frame
c is not added to the tokens
The 256 token rows of the frame do not get c added. Instead c goes to every block. There a Linear called ada turns it into shift, scale and gate. The same c reaches all 16 blocks.
One row c per frame, made from its key and its noise level. Past keys are not dropped: every frame has its own c. The next page shows where c is used. Idea from: Transformer (Vaswani et al., 2017) and DDPM (Ho et al., 2020), sine and cosine features of the noise level. Also Diffusion Forcing (Chen et al., 2024), a noise level per frame; GameNGen (Valevski et al., 2024), noise on the past.
Chapter B7 B6 made c. Here is where c is used.
How does one row c steer 256 rows, and why do the gates start at zero?
c becomes six rows in every block
A block is one attention half plus one MLP half. The model stacks 16 blocks with the same code but different weights. Each block has its own Linear called ada. It turns the same c into six rows of 768.
The six rows are shift, scale and gate for the attention half, then the same three for the MLP half.
Shift and scale on one real row
This is one real token row in block 1. The six numbers are the ones where the gate matters most. LayerNorm moves its 768 numbers to mean 0 and spread 1. Then each number is multiplied by 1 plus scale, and shift is added. Six numbers are shown.
All 256 rows get the same six rows
Every token row of the frame is multiplied by the same scale and shifted by the same shift. The row is copied 256 times. So c steers the whole frame at once.
The gate decides how much is added
After attention, the result is multiplied by the gate and added to x. Where a gate is near 0, the token does not move. The six numbers shown have big gates on purpose. A typical gate1 in block 1 has size 0.009. The result comes from this one frame alone, with no past.
Fail first: random ada weights
We built a fresh stack of 16 blocks and sent this frame through it. We tried random ada weights of size 0.02 and of size 0.1. The larger ones push the output far from the input. With zero weights it stays identical, by construction.
0.78match after 16 blocks, size 0.1
0.99match, size 0.02
So the gates start at exactly zero
Lines 22 to 24 of block.py set the weights and bias of ada to zero. A new block adds nothing and passes x straight through. Training opens each gate only as far as it helps.
This is adaLN-Zero, from the DiT paper (Peebles and Xie, 2022).
c steers each block through shift, scale and gate. Shift and scale reshape the numbers. The gate decides how much is added back. Gates start at zero, so a new block does nothing. Idea from: DiT (Peebles & Xie, 2022), adaLN-Zero; FiLM (Perez et al., 2017), scale and shift from a condition.
Chapter C1 Block 8 (blocks count from 0), one real row, one real pass.
What are q, k and v, and what is each one for?
One row goes in
The frame is a table of 256 rows. Each row has 768 numbers. The model reads frame 326 with half noise on it. We follow row 164, the square with the bird.
One layer makes three copies
The layer qkv turns the 768 numbers into 2,304. That is three lists of 768 for this row: q, k and v.
2,304numbers made per row
1.77 Mlearned numbers in qkv
Cut each list into 12 heads
Each list of 768 is cut into 12 pieces of 64. A piece is one head. The code only relabels the numbers.
RMSNorm sets the size
Each head of q and k is rescaled to a steady length. Head 9 of q shrinks from 10.2 to 8.0. This happens before attention.
What each one is for
The row q asks a question. The row k says what a row offers. The match q·k says how much to read. The row v holds what is read.
Every row does the same
All 256 rows of the frame make their own q, k and v at the same time. With 16 frames that is 4,096 rows in every head.
q asks, k says what a row offers, v is what you take. One layer makes all three from the same row, and 12 heads each get 64 numbers. Idea from: Transformer (Vaswani et al., 2017), q, k, v, heads; ViT-22B (Dehghani et al., 2023), q-norm.
Chapter C2 You have q, k and v for every row (C1).
How does one row read the other rows?
A toy: one reader, four rows
The reader has a q. Each row to read has a k and a v. We want to know how much to take from each row.
q·k says how well they match
Multiply the matching numbers of q and k, then add. A big result means a good match. A negative result means none.
Softmax turns matches into shares
Each match goes through e to the power of it, then divides by the sum. The shares are positive and add to 1.
Take the shares of the v rows
Multiply each v row by its share and add them up. The result is the reader's new row. The shares only weigh; v is the content.
The real scale: 4,096 rows
Row 164 reads 15 frames of 256 rows, plus its own 256. Head 9 puts 17.6% on one square: the one just left of the bird, in frame 323.
4,096rows read per row (play)
17.6%on one square, frame 323
12 heads, 12 readings
Each head does the same steps on its own 64 numbers. The 8 is the square root of 64. It is not the cut into heads.
Glue, mix, gate, add
The 12 results sit side by side as 768 numbers. A Linear layer mixes them. The gate scales them, and the sum goes back into the row.
Attention is a weighted mix of v rows. The shares come from q·k, and each of the 12 heads mixes on its own 64 numbers. Idea from: Transformer (Vaswani et al., 2017), attention, softmax shares, scaling by 8.
Chapter C3 q·k picks which rows to read (C2). Now: how it sees time.
How does attention know which frame a row came from?
Fail first: no positions
Take the k of the bird's square in each of 16 frames, before any rotation. Frames 312 and 318 give q·k 16.4 and 16.6, yet one is 14 frames back and one is 8. No distance is in there.
Toy: rotate 2 numbers by a position
Rotate q by its position m and k by its position n. Press "+1 to both". The arrows turn, but q·k does not change. It depends on m − n.
A real k: 64 numbers, 32 pairs
Number i and number i+32 form pair i. Each pair is a 2-number arrow. Pair 0 turns fast. Pair 31 hardly turns. Move n to see it.
Real frames, labelled by position
Frames in the window get positions 0 to 15. The real q sits at m = 15. With the same q and the same k, one distance always gives the same q·k, here 19.18.
19.18m = 15, n = 14
19.18m = 100, n = 99
pos is a label on every row
The 4,096 rows are one flat list. A second list, pos, holds the frame number of each row. All 256 squares of a frame share it.
The function, line by line
The code is the slide, for 32 pairs at once. angle is position times speed. The last line rotates every pair.
Where it is called
The block rotates q and k, not v. In training, both copies use positions 0 to 15. Playing keeps k rows un-rotated; page H3 shows why.
Rotate by the frame number. Then q·k depends only on how many frames apart two rows are. Idea from: RoFormer (Su et al., 2021), rotate pairs of numbers by position.
Chapter C4 Attention is done (C2). What does the block do next?
What happens after attention, and how do 16 blocks stack?
The MLP half, row by row
The second half of the block is an MLP. It works on each row alone. It widens 768 numbers to 3,072, bends them, and narrows them back.
GELU is the bend
Before GELU, 86% of the 3,072 numbers are negative. GELU turns them to about 0. Without the bend, two Linear layers equal one.
86%negative before GELU
Add back, never replace
Each half computes a correction and adds it to the row. Row 164 enters block 8 at length 25.5 and leaves at 16.2. The shape stays the same.
One c, 16 gate sets
The same c goes to all 16 blocks. Each block has its own ada layer, so its own shift, scale and gate. All gates start at 0.
Blocks in a chain, heads inside
A block is a stack of layers. The 16 blocks run one after another. Inside each block, 12 heads run side by side. That is 192 heads.
Count the numbers
One block holds 10,628,480 learned numbers. The ada layer holds one third of them. The 16 blocks hold 98.6% of the model.
10.6 Mnumbers in one block
98.6%of the model is blocks
A block reads, then computes, and adds both back. Sixteen of them make 98.6% of the model, and x keeps its shape throughout. Idea from: DiT (Peebles & Xie, 2022), adaLN-Zero gates; FiLM (Perez et al., 2017); GELU (Hendrycks & Gimpel, 2016).
Chapter C5 Block 15 (the last, counting from 0) hands back 256 rows of 768.
How do rows turn back into a picture of slopes?
The head is not another transformer
After block 15, one LayerNorm, a shift and scale from c, and one more Linear layer make the output. Each row is handled alone. This head is not an attention head.
One row: 768 numbers become 192
The Linear layer gives 192 numbers per row: red, green and blue, 64 each. They fold back into three grids of 8 × 8.
All rows: a picture of slopes
Put all 256 squares together and the output has the shape of a frame. It is not a frame. It holds a slope for each pixel. Part E explains slopes.
Both layers start at zero
The two head layers start at exactly 0. A brand-new model outputs 0 everywhere. Its loss is 1.28. Training moves it from that calm start.
1.28loss of a model that outputs 0
0.00005trained, this frame
The head makes slopes, not pixels. One Linear layer maps each row to 192 numbers, and it starts at zero. Idea from: DiT (Peebles & Xie, 2022), zero-initialised final layer.
Forward pass 1 The real trained model, run once. Every picture on these pages is a real tensor.
What goes in, and what comes out?
Left to right: the real next frame, noise, the input z, the model's output, and the frame it draws.
The frame to draw
The game is running. The model holds 15 memory frames and must draw the next frame for key 0, no tap. This pass works on a frame that is half real picture, half noise. We call it z, and its noise level is t = 0.5.
What comes out
Out comes the slope: one number per pixel. It points from the real frame towards the noise. The slope is the direction from the real frame towards the noise. A later page shows it on one pixel.
One step removes the noise
Take z, then subtract half the slope: z − 0.5 · slope. z moves against the slope, away from the noise. Half of z was the real frame, so this one step is easy. Real play starts from pure noise.
The road, in shapes
Each chip is one stop, with the shape of the numbers there. Click a chip to jump to its page. We follow the bird's square, number 132, along this road.
All of it is learned numbers
Everything between the left picture and the right picture is done by learned numbers. The next pages open every stop and show the real values of this one pass.
172,518,848learned numbers in the model
One pass is one subtraction away from a frame. A half-noise frame goes in, a slope comes out, and z − 0.5 · slope is the drawn frame. Idea from: Rectified Flow (Liu et al., 2022), the slope and one subtraction.
Forward pass 2 Before this pass, the 15 memory frames were each run through the model once.
What does the model already hold?
The 15 pictures are only labels. The model keeps k and v rows, one shelf per block.
15 memory frames, each with its key
Before this pass, each memory frame went through the same 16 blocks once. It had t = 0.05 and the key that made it. Here all 15 keys are 0, no tap. This pass only reads what they left behind.
One frame is 256 rows
A frame of 128 × 128 pixels is cut every 8 pixels into 16 × 16 squares. That is 256 squares. Each square becomes one row of 768 numbers.
15 frames are 3,840 rows
Stack the 15 frames: 15 × 256 = 3,840 rows. Frame 1 holds rows 0 to 255, frame 2 holds rows 256 to 511, and so on.
Every block keeps k and v
In every block, each frame leaves two tables behind: k and v, 256 rows each. One shelf per block holds all 15 frames. The grid shows 16 shelves × 15 frames.
94,371,840numbers in memory (k and v)
Block n reads its own shelf
The new frame has its own 256 rows. Block n reads the 3,840 saved rows of shelf n plus these 256 new rows. That is 4,096 rows.
Memory is 16 shelves of k and v. Each shelf holds 15 frames × 256 rows, saved before this pass starts. Idea from: CausVid (Yin et al., 2024), keep k and v of past frames.
Forward pass 3 Read it top to bottom. Blue is the past. Yellow is this frame's key and noise level.
Where do the past frames and the key go in one pass?
The past, in blue
On the left are the 15 past frames, each with the key that made it. Each frame went through the same 16 blocks once, earlier. Every block kept that run's k and v rows: 15 × 256 = 3,840 rows per block.
This frame, in yellow
The new frame z is half real, half noise. Its key and its noise level t make c, one row of 768 numbers. The squares of z become x, a table of 256 rows × 768.
One transformer: 16 blocks
The middle column is the only transformer. It is 16 blocks, one after another. Each block has an attention half and an MLP half, and x keeps the shape 256 × 768.
Where the past and the key enter
Blue arrows: block n's attention reads its own 256 rows plus the 3,840 saved rows of block n. Yellow arrows: c sets scale, shift and gate in every block. This is the only place the past and the key enter.
The head is not a transformer
After block 16, one small layer turns each row of 768 into 192 pixel values. A reshape puts the 256 squares back into a 128 × 128 picture. There is no second transformer.
One subtraction draws the frame
z minus half the model's output is the new frame. The new frame is then pushed into memory and the oldest one is dropped, so the loop repeats.
The past enters in one place. Attention in each block reads that block's saved rows. Nothing else carries the past. Idea from: CausVid (Yin et al., 2024), saved k and v. DiT (Peebles & Xie, 2022), c in every block.
Forward pass 4 We follow one square, the bird's square, from pixels to the model's answer.
How does a picture become 256 lists of 192 numbers?
Red, green, blue: numbers 0–63, 64–127, 128–191. Hover a cell to read its number.
Pixels become −1 … 1
Each pixel value p, from 0 to 255, first becomes p / 127.5 − 1, a number from −1 to 1. The top-left red pixel of the bird's square is 78, so it becomes −0.388. In z it is mixed with noise and reads −0.299.
Cut every 8 pixels
The 128 × 128 frame is cut every 8 pixels into 16 × 16 = 256 squares. The code calls a square a patch. Square 0 is top left and square 255 is bottom right.
The bird's square is number 132
Count 8 squares down and 4 across: 8 × 16 + 4 = 132. It covers pixel rows 64 to 71 and columns 32 to 39. Enlarged, it is 8 × 8 pixels.
Three colours: 192 numbers
Every pixel has a red, a green and a blue number. So the square holds 3 × 8 × 8 = 192 numbers. These are the real values of z, colour by colour.
Laid end to end
The 192 numbers go into one line: the red rows first, then the green rows, then the blue rows. This line is the square's list. Red is positive and blue is negative.
Every square becomes such a list
All 256 squares do the same, so the frame becomes 256 lists of 192 numbers. The shape is [1, 256, 192]. This step has no weights: numbers only move.
256squares per frame
192numbers per square
A picture is a table of 256 lists. One list per square, 192 numbers each, and no learned weights yet. Idea from: ViT (Dosovitskiy et al., 2020), patches.
Forward pass 5 One Linear layer and one position table turn the squares into the table x.
How does a square's list of 192 numbers become a row of 768 that knows its place?
Red is positive, blue is negative, white is near 0. Hover any strip to read a number.
One Linear: a table of 768 rows
The weight is a table of 768 rows and 192 columns. With the bias it holds 148,224 learned numbers. All 256 squares use this same table.
Output 0 is row 0 times the square
Multiply row 0 pairwise with the 192 numbers of the square, then add. That gives the 192 products below. Their sum plus bias 0 is output number 0.
768 outputs, one per row
Rows 1 to 767 do the same, each with its own 192 weights. The result is 768 numbers for this square. The model's own output 0 matches the sum we did by hand.
Add the square's position
The model also learns one row of 768 per square place. That table is pos_s, and its row 132 is added to the square's list. Hover a column: the three numbers line up, and the bottom one is the sum.
All 256 squares: the table x
The same steps run for all 256 squares at once. The result x has 256 rows, one per square, and 768 columns. This table enters block 1.
256 × 768the table x
Each square is now a row of 768 that knows where it sits. The shared Linear table gives the content. The position row gives the place. Idea from: ViT (Dosovitskiy et al., 2020), patch embedding.
Forward pass 6 The frame's key and noise level make one row of 768 numbers, c.
How do key 0 and t = 0.5 become one row c?
Hover any strip to read a number. In the sum and c strips the hover shows the arithmetic.
t becomes 256 numbers, no weights
The noise level t = 0.5 becomes 128 cosines and 128 sines of different speeds. The first cosine is cos(500) = −0.884. This step uses no learned weights.
A small network: tmlp
Two Linear layers with a SiLU between them turn the 256 numbers into 768. The first layer is 256 → 768. Then SiLU, then 768 → 768.
The key picks a learned row
The table act holds one learned row per key. Key 0 uses act[0]. Row 1 is for a tap, so this frame does not use it.
Add, then SiLU: this is c
Add the two rows, then apply SiLU, the curve on the right. It squashes big negative numbers to about 0 and lets positive numbers stay. The result is c.
c is one row for the whole frame
The shape [1, 1, 1, 768] has a 1 where the 256 squares would be. Wherever c is used, that one row is copied to all 256 squares. The key and t reach every block through c.
c carries the key and the noise level. It is one row of 768 numbers for the whole frame, not one per square. Idea from: Transformer (Vaswani et al., 2017), sinusoidal features of t. DiT (Peebles & Xie, 2022), c from key and t.
Forward pass 7 Until now we followed one square. The model holds all 256 at once.
Why does block 1 work on a table, not on one square?
Row 132, marked in yellow, is the bird's square. Hover a table to read row and column.
x is a table: 256 rows × 768 columns
One row per square, 768 numbers per row. B is games, F is frames in this pass, N is squares and W is numbers per square. Here B = 1 and F = 1.
Most steps work on each row alone
The same formula runs on row 0, row 1, up to row 255, separately. LayerNorm, scale and shift, qkv and the MLP all work this way. No row looks at another row.
Only attention lets rows read rows
This is where the bird's square looks at its neighbours and at the past. The next pages open that step. First we need c, cut into six rows.
c becomes six rows
Block 1's own ada layer turns c into 4,608 numbers. They are cut into six rows of 768. None of the six depends on the square.
One row, used by all 256 squares
The same row serves every square. Below, the row 1 + sc1 is copied onto 256 rows. The row has size 1 where the squares go, so it is applied to every row. Every row is identical.
Rows work alone, except in attention. The six rows from c are the same for every square. Idea from: DiT (Peebles & Xie, 2022), adaLN-Zero (shift, scale, gate from c).
Forward pass 8 Block 1, first lines: normalise each row, then use c to scale and shift it.
How does each row get normalised, scaled and shifted?
Red is positive, blue is negative. The yellow marks show row 132, the bird's square.
LayerNorm: each row on its own
Each of the 256 rows gets its own mean and spread. Every number becomes (value − mean) / spread. After this, every row has mean 0 and spread 1.
Scale: one factor per column
Column j of every row is multiplied by the same number, (1 + sc1)[j]. The middle table shows that factor copied to all 256 rows. So its rows are identical.
Shift: one number per column
Then column j gets the same sh1[j] added in every row. The result is y. Here j is only a column number, from 0 to 767.
Row 132, number by number
Here is the bird's row as four strips. Hover a column to read the sum. The first strip times the second, plus the third, gives the fourth.
Same formula for every row, with the same column numbers. y = LayerNorm(x) · (1 + sc1) + sh1. Idea from: LayerNorm (Ba et al., 2016). DiT (Peebles & Xie, 2022), scale and shift from c.
Forward pass 9 The table y goes through one Linear. Then its columns are cut into 12 heads.
How are q, k and v cut into 12 heads?
Every table has all 256 rows. Only the columns are cut. Hover to read a column name.
One Linear makes q, k and v
Each row of y, 768 numbers, is multiplied by a weight table of 768 × 2,304. So each row becomes 2,304 numbers. The table is now 256 × 2,304, still one row per square.
view only gives names
view changes no number. It names the columns: 0–767 are q, 768–1535 are k, and 1536–2303 are v. Inside each, columns 0–63 are head 1, 64–127 are head 2, and so on.
q is cut into 12 groups of 64
Take the q columns alone: a table of 256 × 768. Its columns form 12 groups of 64 numbers. Each group is one head.
permute: 12 tables of 256 × 64
permute moves the head number to the front. Now q is 12 separate tables of 256 × 64. Every head still holds all 256 squares, because rows are never split.
The 12 heads run side by side
Each head runs the same attention on its own 64 columns. That is all "multi-head" means. The tables k and v are cut in the same way.
Heads are column slices, not row slices. 12 heads × 64 columns = 768 columns, and every head sees all 256 squares. Idea from: Transformer (Vaswani et al., 2017), q, k, v and heads.
Forward pass 10 This happens before attention, not after it.
Why are q and k normalised before attention?
Head 1 only. Each bar is the length of one q row. The bird's row is red.
Attention has not happened yet
At this point q, k and v are only three tables. Attention comes next and multiplies q rows with k rows. If a q or k row is very long, its scores become huge.
Row lengths differ a lot
These are the lengths of head 1's 256 q rows in block 1, before qn. They run from 8.14 to 16.24. Long rows give big scores.
RMSNorm fixes the length
qn divides each 64-number row by its root mean square, then multiplies by 64 learned weights. After it, all rows have about the same length: 6.85 to 6.97.
Why: scores stay small
Softmax uses exp(score). Without the norm the scores run from −4.55 to 18.21, and exp(18.2) is 81 million times exp(0). A few squares would dominate.
Then rope turns each row by its frame
A table row has no time of its own. So rope turns each row by its frame number. Memory rows get 0 to 14, the new frame gets 15.
qn and kn keep the scores in a safe range. With them, head 1's scores in block 1 run from −2.09 to 5.74. Idea from: ViT-22B (Dehghani et al., 2023), q/k norm. RoFormer (Su et al., 2021), RoPE.
Forward pass 11 Here the rows finally meet. This is attention, step one: scores.
How does every square score every square it may read?
Left: the loop in plain slices. Right: head 1 only, the first pass of that loop, with real numbers.
Multi-head attention is a loop over 64 columns
Head h owns 64 of the 768 columns. It scores q against k on those columns only and divides by 8. Then it takes a weighted sum of the v columns.
The model never runs this loop
The calls view and permute lay the 12 slices out as 12 tables. One call then computes all 12 heads at once. It matches the loop to 3.1e-06. One big head over 768 columns differs by 0.611.
12 heads, 12 slices
Each card is one head: the columns it owns and the real range of its scores. Each head writes its 256 × 64 result next to the others.
The 8 comes from the head width
8 is the square root of 64, the width of one head. Every head divides its scores by 8, so a dot product of 64 numbers does not grow too large. The cut into heads is made by the calls view and permute.
Head 1: scores = q · kᵀ / 8
q is 256 × 64. The table k, turned on its side, is 64 × 4,096. Their product is 256 × 4,096 scores.
k has 4,096 rows
It holds the 3,840 stored rows of the 15 memory frames plus this frame's 256 rows. Entry (r, j) is row r of q times row j of k, divided by 8. So every square scores every square of every frame.
Each square gets a score for 4,096 squares. That includes its neighbours and the whole past. Idea from: Transformer (Vaswani et al., 2017), scores and the 1/8 scale.
Forward pass 12 Attention, step two. Each row of scores becomes shares.
How does softmax turn scores into shares that add up to 1?
Head 1, block 1. Darker red means a bigger share. The scale is stretched to show small shares.
A toy row of three scores
Take the exp of each score, then divide by the sum of those three. The three shares are positive and add up to 1. A bigger score gets a bigger share.
The real shares of head 1
Softmax runs along each row of the 256 × 4,096 score table. Each row becomes 4,096 shares. The columns are grouped by frame: memory 1 to 15, then the new frame.
The bird's row, back on the 16 frames
Row 132 holds 4,096 shares, one per square it may read. Put them back onto the 16 frames. Darker means a bigger share, and the numbers are the share of each frame.
Every row adds up to 1
Every row sums to 1 within 0.00001. The largest single share anywhere in head 1 is 0.09%. So head 1 spreads its attention widely.
Shares say how much to take, not what. They are only weights. The content comes from v, on the next page. Idea from: Transformer (Vaswani et al., 2017), softmax shares.
Forward pass 13 Attention, step three. The shares pick how much to take from each v row.
How does each square take a weighted average of v rows?
Real numbers from the t = 0.5 pass. The first four of 64 numbers of each v row are shown.
Shares say how much, v says what
Shares are only weights. The table v holds the content: 64 numbers for each token. The tables k and v come from different columns of qkv.
A toy example
Three tokens, with v rows of two numbers. Each v row is multiplied by its share, and the results are added. That gives the new row o.
Real: a sharp head
Block 9, head 12, in the t = 0.5 pass. The bird's row takes 69% from five tokens, all in memory. The two biggest are the square to its left, one and two frames ago.
Real: a flat head
Block 1, head 1, same pass. In the bird's row the largest share is 0.053%. So its row o is close to a plain average of almost all 4,096 tokens.
The tables v and o for head 1
Row r of o is the 4,096 rows of v, each multiplied by its share in row r, then added. Every square makes its own mix.
12 heads side by side
Every head does this. The 12 tables of 256 × 64 are put back side by side: 256 × 768 again. The calls transpose and reshape only move numbers into that order.
Each square's new row is a weighted average of v rows. This is the step that copies from the past. Idea from: Transformer (Vaswani et al., 2017), weighted sum of v.
Forward pass 14 The end of block 1's attention half.
How are the heads mixed, gated and added to x?
Every table is 256 rows × 768 columns. The yellow marks show row 132.
proj mixes the columns of each row
The layer proj is a Linear(768, 768) applied to each row of o. It mixes all 768 columns, so the 12 heads can share what they read. The result is the table proj(o).
The gate g1 decides how much is kept
g1 is one row, the same for every square. Each column of proj(o) is multiplied by its g1 number. Most of g1 is near 0.
The change
The product is the change this half makes. Only a few columns are big, because only a few g1 numbers are big.
Add the change to x
The change is added to x. The result is x after the attention half. Most columns of x hardly move.
The gate is a dial per column. A gate near 0 leaves that column of x untouched. Idea from: DiT (Peebles & Xie, 2022), the gate.
Forward pass 15 The second half of block 1. No row looks at another row here.
What does the MLP do to each row: 768 → 3,072 → 768?
The 3,072-column tables are drawn at the same width, so each column is thinner.
Same lines, new rows: sc2 and sh2
The MLP half starts like the attention half: LayerNorm, then scale and shift. This time it uses sc2 and sh2 from c. The result is the table y2.
Each row grows to 3,072
A Linear layer turns each row of 768 numbers into 3,072 numbers. No row looks at another row. The table is now 256 × 3,072.
GELU sets negatives to about 0
GELU works on each number alone. A negative number becomes about 0 and a positive number stays. In the bird's row, 780 of the 3,072 numbers stay above 0.01.
The row shrinks back to 768
Another Linear layer turns each row of 3,072 back into 768 numbers. The table is 256 × 768 again. This is the MLP's output.
Gate g2, then add to x
The output is multiplied by g2, one row for all squares. Then it is added to x. The result is x out of block 1.
The MLP works on one row at a time. It widens the row, bends it with GELU, and shrinks it back. Idea from: GELU (Hendrycks & Gimpel, 2016). DiT (Peebles & Xie, 2022), the gate.
Forward pass 16 Every step of block 1 is now open. Here it is as one piece.
What does block 1 take in, and what comes out?
A table in, a table out
Block 1 takes a table of 256 × 768 and gives back a table of the same shape. The numbers are different. Look at the yellow row: the bird's square has changed.
One block, as shapes
Read the chips from left to right. Each chip is the shape after one step. Attention is the only step where rows meet.
Checked against the model
We wrote every step by hand outside the model. The result matches the model's own block 1 to the precision stored (difference 0.0). Shares times v match its attention output to 3.1e-06.
The output is the input of block 2
Block 2 repeats every step with its own weights. It uses the same c but its own saved k and v rows.
A block keeps the shape and changes the numbers. That is why 16 of them can be stacked. Idea from: DiT (Peebles & Xie, 2022), the block.
Forward pass 17 One row per block, all real. The yellow box marks the bird's square.
What does each of the 16 blocks do?
Every block runs the same lines with its own weights, its own shelf and the same c. Hover a map to read a square.
Where the bird's square reads
Each row is one block. The bars show how the bird's square splits its attention over the 15 memory frames and the new frame. Blue is memory and red is the new frame.
Its attention on the new frame
This map shows the share the bird's square gives to each square of the new frame. Darker means a bigger share. Block 1 puts 97% on memory, so its map is faint.
What the attention half changed
This map shows how much the attention half changed each square. Darker blue means a bigger change.
What the MLP half changed
The same for the MLP half, in green. Compare the two columns block by block.
The bird's square after each block
The strip is the bird's 768 numbers after the block. The text gives the length of its row going in and the size of both changes.
The size of each change
Attention changes the bird's square most in blocks 1 and 10. The MLP change is largest in blocks 14 to 16. The row length goes up and down: 8.75 into block 1, 41 into block 2, 48 into block 15.
8.75row length into block 1
48.38row length into block 15
Every block changes the bird's row, some a lot. Block 1 and block 10 change it most by attention, and the MLP changes grow towards block 15. Idea from: DiT (Peebles & Xie, 2022), a stack of blocks.
Forward pass 18 After block 16 the table is still 256 × 768. First, what will the head output?
What does the model output: a slope, not a picture?
Four real pixels of this pass. Numbers in brackets are the same pixel on the 0 to 255 scale.
Two different things are called v
Inside attention, v is the values table. The model's output is something else: a number for every pixel that we call the slope. From here on, only the slope is the output.
How the input was made
The input is z = (1 − t) · real + t · noise. At t = 0.5, z is half real frame and half noise. As t goes from 0 to 1, z slides on a straight line from the real frame to pure noise.
The slope is the direction of that line
The slope is noise − real: the direction from the real frame towards the noise. If the model gives it, one subtraction returns the real frame at any t. That is z − t · slope = real. The training loss measures the miss on the slope.
One real pixel: what went in
The top-left red pixel of the bird's square. The real value is 78, which is −0.388. The noise is −0.209, so z = −0.299.
What the model said, and the right answer
The model's slope is 0.199. The right answer, noise − real, is 0.179. The model is close.
One subtraction gives the frame
z − 0.5 × 0.199 gives −0.398, which is pixel value 77. The real pixel is 78. The pixel from elsewhere lands exactly: real 32, drawn 32.
The model gives the direction from the real frame to the noise. Moving z against it removes the noise. slope = noise − realIdea from: Rectified Flow (Liu et al., 2022), slope v = eps − x.
Forward pass 19 After block 16, x is still a table of 256 × 768. There is no second transformer.
How does each row of 768 become 192 pixel values?
Red is positive, blue is negative. The yellow marks show row 132, the bird's square.
x after block 16
The table is still 256 × 768, one row per square. The head does the same three moves as the start of every block. Then it adds one last Linear layer.
LayerNorm each row
Each row gets its own mean 0 and spread 1, as in every block. No row looks at another row.
Scale and shift with rows from c
The layer ada_out turns c into one row sc and one row sh. They are the same for all 256 squares. The table is multiplied by (1 + sc) and sh is added.
Multiply by the weight table 768 × 192
The last Linear layer turns each row of 768 into 192 numbers. Every row is still one square. Its 192 numbers are 64 red, 64 green and 64 blue.
The head is one small layer, not a second transformer. It turns each row into the 192 numbers of its square. Idea from: DiT (Peebles & Xie, 2022), the head.
Forward pass 20 The slope table has 256 rows of 192 numbers. Now it becomes a picture.
How do the 256 squares go back into a picture?
The slope is shown on a scale where red is positive, blue is negative, white is 0.
Row r goes back to its square
The step unpatchify is the exact reverse of the cut at the start. Row r goes to row r // 16 and column r % 16 of the 16 × 16 grid. It has no weights.
The model's table and the right answer
Left: the model's slope table. Right: the right answer, noise − real, in the same layout. They look almost the same.
The same, as pictures
Unpacked into 3 colours × 8 × 8 pixels per square, the tables become pictures. The third picture is the squared error per pixel. Darker means more error.
The bird's square, red channel
The model's slope next to the right answer for the 8 × 8 red pixels of square 132. The numbers are close.
0.0003mean squared error, bird's square
0.00004whole frame
The slope picture is almost exactly noise − real. The head's output is the right answer to within a tiny error. Idea from: ViT (Dosovitskiy et al., 2020), patches in reverse.
Forward pass 21 The last step of the pass. Every pixel of z loses half its slope.
How does one subtraction draw the frame?
The bird's square, red channel, is shown as pixel values from 0 to 255.
z minus half the slope
Every pixel of z minus half its slope. The slope is almost exactly noise − real. So what is left is almost exactly the real frame.
The bird's square in pixel values
Left: the drawn red pixels of square 132. Right: the real ones. The first row is sky, with real value 78. The drawn row reads 77 77 77 79 79 79 78 78.
How close is the frame?
Over the whole frame, the drawn pixel is on average 0.199 steps of 255 away from the real one. Half of z was already the real frame, so this is easy. Real play uses two passes from pure noise.
0.199mean |drawn − real|, 0–255 steps
One subtraction turns the slope into a frame.z + (t2 − t) · slope, with t = 0.5 and t2 = 0. Idea from: Rectified Flow (Liu et al., 2022), one subtraction.
Forward pass 22 The pass you just followed is one step of a longer loop.
What is the whole pass, in one line?
The pass, as shapes
Each chip is one stop with the shape of its numbers. Click a chip to jump back to its page. The bird's square, number 132, went through every one.
In real play: a few such passes
In real play the new frame starts as pure noise at t = 1. It takes a few passes, and each pass moves t closer to 0. Here two passes take it from t = 1 to t = 0.
Then the frame is pushed
One more pass at t = 0.05 stores the frame's k and v as memory. The oldest frame is dropped. The next pages follow one live frame from start to end.
One pass is one step; a frame takes two, and a push. Then the loop starts again with the next key. Idea from: Rectified Flow (Liu et al., 2022). CausVid (Yin et al., 2024).
Live frame 1 Now one real moment of play, from the 15 past frames to the next frame on screen.
Where do the past frames go? Follow one live frame
Every picture and table on the next pages is the real tensor. Yellow chips mark the part a page opens.
The past never goes back in as pictures
These pages follow the code that runs when you play. Each past frame was run through the 16 blocks once. Every block kept that frame's k and v rows on its shelf.
Attention reads the shelf
When the new frame is drawn, every block's attention reads its shelf. That is 3,840 past rows next to the 256 new rows. It is the only place the past enters.
The loop in plain lines
A frame takes two passes, from t = 1 to 0.5 to 0. Then one more run at t = 0.05 pushes it into memory. The oldest frame leaves.
One live frame, start to end
The new frame starts as pure noise. After pass 1 it is half noise. After pass 2 it is the frame. Compare it with the real next frame.
The replica was checked
This replica of the play code was checked against the real Streamer with the same random seed. The difference is 0.0. It used 2 passes and memory noise t = 0.05.
The past enters through the shelves, and only there. Each block reads its own shelf of saved k and v rows. Idea from: CausVid (Yin et al., 2024), saved k and v.
Live frame 2 The 15 pictures are used once, when each is pushed.
What happens to a memory picture when it is pushed?
The noise goes onto the pixels of the picture. It never goes onto k or v.
15 pictures, 15 keys
These are the last 15 frames the game showed, each with the key that made it. Here all keys are 0, no tap. The model does not keep these pictures.
Each picture is used once
A picture is used once, when it is pushed. What the model keeps is the k and v rows that the push leaves in every block. The pictures only show which frame each set of rows came from.
Pixels become −1 to 1
Each pixel goes from 0 … 255 to −1 … 1 by value / 127.5 − 1. This is the same rule as for any frame.
The noise goes on the pixels
A push first mixes the picture with a little noise, t = 0.05. Both pictures below are real. The noise is never added to k or v, because they are computed afterwards.
Why a little noise
In play, the past is the model's own drawings, which are slightly off. The model was trained on a slightly noisy past, so it can read an imperfect past. Errors can still pile up, and a later training stage works on that.
Kept once, read many times
Without the shelf, each pass would run all 15 pictures again, plus the new frame. That is 4,096 rows through the 16 blocks, twice per new frame. With the shelf, only the new frame's 256 rows go in. Two draw passes and one push make 768 rows in all, 10.7 times fewer. The block still gets 256 rows and gives back 256 rows on every pass.
Why the saved rows stay right
A saved frame's rows may read only older frames and themselves. They never read the new frame, so drawing it cannot change them. Running them again would give the same k and v. We checked this on 15 real frames of one game. The biggest difference between both ways was 0.00003, which is rounding.
Noise goes in at the pixels, once per push. k and v are made from the slightly noisy picture. Every later pass reads them and never computes them again. Ideas from: GameNGen (Valevski et al., 2024), a little noise on the past; CausVid (Yin et al., 2024), keep k and v of past frames.
Live frame 3 How a finished frame becomes memory.
What does a push compute?
One push fills one column of the memory grid: the same frame, in all 16 shelves.
A push is one run through 16 blocks
The pushed frame is cut into 256 squares like any frame. It runs through all 16 blocks with its own key. In each block it makes q, k and v.
Each block adds to its shelf
Its attention reads the frames already on the shelf, plus itself. Then the block appends this frame's 256 k rows and 256 v rows to its shelf. The k rows are stored before the time rotation.
The output is thrown away
The head runs during a push, but its output is not used. Only the k and v rows matter.
Saved rows carry older frames too
Block n's k and v for this frame come from what block n − 1 gave it. Block n − 1 already read the older frames' rows. So each frame's rows also carry some of the frames before it.
The shelf grows 256 rows per push
At the start of the game the 15 frames were pushed one after another. Every shelf grew by 256 rows each time, up to 3,840 rows.
A push costs one more run per frame. Its only product is 256 k rows and 256 v rows in every shelf. Idea from: CausVid (Yin et al., 2024), saved k and v.
Live frame 4 This is the whole memory the model uses.
What is on the shelves?
Real tables for blocks 1, 9 and 16. The thin lines split each table into 15 frames of 256 rows, oldest at the top.
One shelf per block
Each block has its own shelf. A shelf holds a k table and a v table, 3,840 rows × 768 each. The code stores the 768 numbers as 12 heads × 64.
Real shelves: blocks 1, 9 and 16
Each table shows 15 bands of 256 rows. Each band is one frame. The top band is the oldest frame, and the bottom band is the newest.
16 shelves, in plain numbers
One shelf is 3,840 × 768 for k and the same for v. With 16 shelves, k has 47,185,920 numbers and v has 47,185,920 numbers.
94,371,840numbers in memory
Memory is 16 shelves of k and v. Nothing else carries the past. Idea from: CausVid (Yin et al., 2024), saved k and v.
Live frame 5 You press a key. The new frame begins as pure noise.
What enters block 1 when the new frame is pure noise?
Real tensors of pass 1, t = 1. The yellow triangle marks the bird's square, number 132.
The new frame starts as pure noise
The new frame begins as random numbers, one per pixel per colour. That is z, with shape 3 × 128 × 128 and noise level t = 1. It holds no picture.
Everything must come from the shelves and the key
Nothing in z tells the model where the pipes or the bird are. Whatever appears in the frame has to come from the shelves and the key.
z becomes the table x
z is cut into 256 squares of 192 numbers. Each goes through Linear(192 → 768) and gets its position row added. That gives x: 256 rows × 768.
The key and t make c
Separately, the key row and the noise level t = 1 make c: one row of 768 for the whole frame. Below: the 256 features of t, the key row and c.
Block 1 starts with a table of pure noise and one row c. The past is not in there yet. Idea from: DiT (Peebles & Xie, 2022), c.
Live frame 6 Every block runs the same lines with its own weights. We open block 9.
Block 9: what does c do to the table?
Pass 1, real tensors. The six strips are block 9's own rows from c. The yellow triangle marks square 132.
c becomes six rows
Block 9's ada layer turns c into six rows of 768. They are sh1, sc1, g1, sh2, sc2 and g2. The same six rows act on all 256 squares.
LayerNorm, then scale and shift
LayerNorm gives each of the 256 rows mean 0 and spread 1. Then column j is multiplied by (1 + sc1)[j], and sh1[j] is added.
The table before and after
Left: x entering block 9. Right: y, after LayerNorm, scale and shift. Both are 256 rows × 768 columns.
c is the same in every block, and each block reads it with its own weights. Block 9's rows differ from block 1's. Idea from: DiT (Peebles & Xie, 2022), adaLN-Zero.
Live frame 7 Inside block 9's attention, the shelf is glued on top of the new rows.
Block 9: where do the past rows join?
Pass 1, real tensors of block 9. Above the red line in K: the 3,840 shelf rows. Below it: the 256 new rows.
q, k, v for the 256 new rows
The layer qkv turns each row of y into q, k and v. Each has 256 rows, cut into 12 heads of 64. They come from the new frame only.
q and k are normalised
In head 1, the row lengths fall from 6.27 – 15.61 to 8.42 – 8.77. This keeps the scores in a safe range.
Here the past joins
Shelf 9's 3,840 rows go on top of the new k and the new v. That makes K and V with 4,096 rows each. The red line marks where the shelf ends.
Then the time rotation
Rope turns every row by its frame. Memory frames get positions 0 to 14, and the new frame gets 15. The shelf keeps plain keys, and every pass numbers the frames again.
The past joins as extra rows of K and V. The new rows only ask. The shelf rows answer. Idea from: CausVid (Yin et al., 2024). RoFormer (Su et al., 2021).
Live frame 8 Head 12 of block 9, pass 1 (t = 1, pure noise), reads memory squares next to the bird.
Block 9: which rows does the bird's square read?
Pass 1, block 9. Darker means a bigger share. Hover a map to read a square.
256 × 4,096 shares in head 12
Each of the 256 new q rows is compared with all 4,096 K rows. Softmax turns each row of scores into shares that add up to 1. Darker means more.
The six rows the bird reads most
These are the six largest shares in the bird's row of head 12. Five are memory squares, mostly the square left of the bird in the last frames. One is in the new frame.
Back on the 16 frames
The bird's whole row of head-12 shares, put back on the 16 frames. Red means more. The new frame's squares are drawn on the finished frame.
All 12 heads
This is the percent of the bird's attention that each head puts on each frame. Heads 2, 3, 4, 5, 9 and 11 put 86 to 97% on the new frame. The other six read mostly memory, and head 12 puts 75% on it.
Different heads read different places. Head 12 reads memory squares next to the bird. Idea from: Transformer (Vaswani et al., 2017), softmax shares.
Live frame 9 Mix the v rows, add to x, then the MLP, then add again.
Block 9: what comes out?
Pass 1, block 9, real tensors. The yellow triangle marks square 132.
Mix v, join the heads, gate, add
Each new row becomes the share-weighted sum of the 4,096 V rows. The 12 heads are put side by side, mixed by proj, multiplied by the gate g1 and added to x.
The MLP works on each row alone
The MLP takes each row from 768 to 3,072 and back to 768. It is multiplied by g2 and added to x. Out comes the new x, still 256 × 768.
The bird's row through block 9
The numbers below are the first 8 of the bird's row at each stage. The change is mostly in other columns. Of these first 8, only column 5 moves much.
Block 9 hands a changed table to block 10. The bird's row now holds a mix of what it read. Idea from: Transformer (Vaswani et al., 2017). DiT (Peebles & Xie, 2022).
Live frame 10 Every block repeats the same lines with its own weights and its own shelf.
Pass 1: what do all 16 blocks do?
Pass 1, real numbers. Block 9 is highlighted: it is the block we opened.
Where the bird reads, block by block
For each block: how the bird's square splits its attention between the 15 memory frames and the new frame. It is the average over 12 heads.
How big each change was
The last three columns give the size of the row going in, the attention change and the MLP change. They differ a lot from block to block.
Where attention changed the frame
These maps show where each block's attention half changed the 256 squares. Blue means a bigger change. The box marks the bird's square.
Thirteen of the 16 blocks put 81 to 98% of the bird's attention on memory. Blocks 9, 15 and 16 put 44%, 61% and 71%. Idea from: DiT (Peebles & Xie, 2022).
Live frame 11 After block 16, pass 1 ends. One step moves z from t = 1 to t = 0.5.
How does the head turn 256 rows into one step of z?
Pass 1, real tensors. The yellow triangle marks the bird's square.
x after block 16, and the head
After block 16, x is still 256 × 768. The head gives each square 192 pixel values. LayerNorm, scale and shift from c, and Linear 768 → 192 do this.
The squares go back into a picture
The 256 × 192 values are put back into a 3 × 128 × 128 picture: the slope. It points from the real frame towards the noise.
One step: z + (0.5 − 1) × slope
The step goes from t = 1 to t = 0.5. Half of the slope is subtracted from z, so z moves against it. The frame is now half noise.
The bird's square in numbers
These are the first four red pixels of the bird's square, row 64. After pass 1 they are only half way to the real value, −0.388. The line below shows them after pass 2.
Pass 1 takes z from pure noise to half noise. Pass 2 takes it the rest of the way. Idea from: Rectified Flow (Liu et al., 2022).
Live frame 12 The same blocks and the same shelves run again, with a new c.
Pass 2: how does the frame finish?
Real tensors. Pass 2 starts at t = 0.5 and ends at t = 0, clamped to −1 … 1.
The shelves stay, only c changes
z, now half noise, goes through the whole stack again. The shelves are not changed. Only c differs, because t is now 0.5.
The second step finishes the frame
The second slope takes z the rest of the way: z + (0 − 0.5) × slope. The result is the frame at t = 0, near the real pixel values.
Both passes, block by block
Compare how much of the bird's attention goes to memory in each pass. Compare the size of the changes too.
Drawn against real
Two passes from pure noise give the drawn frame on the left. The real next frame is on the right.
0.73mean |drawn − real|, 0–255 steps
Two passes from pure noise draw a frame within about 0.7 of 255 of the real one. The picture came from the past and the key. Idea from: Rectified Flow (Liu et al., 2022), a second step.
Live frame 13 A test: same noise, same key, a different past.
Is it really the past that draws the frame?
Real pictures. Past B is a different moment of the same game.
Change only the past
The new frame starts as the same pure noise, with the same key, both times. Only the 15 past frames differ. The noise holds no picture.
Past A: the moment of this page
The 15 past frames plus the noise go in. The model draws the frame on the right of the arrow. Next to it is the real next frame.
Past B: a different moment
Same noise, same key. Only the past changes. The model now draws a different frame, with pipes and the bird in other places.
Both drawings match their real frame
The error is 0.24 for past A and 0.23 for past B on the 0 to 255 scale. The noise holds no picture. So the picture came from the past frames and the key.
0.24error, past A
0.23error, past B
The live frame has a different error
This test uses 4 passes and a clean past. The live player uses 2 passes and a past at t = 0.05. Its error is 0.73 on a different frame. We did not test which change matters.
Change the past and the drawing changes. The lookup in every block's attention does the copying. Idea from: CausVid (Yin et al., 2024), the past enters through k and v.
Live frame 14 The drawn frame becomes memory. The loop starts again.
What happens to the new frame afterwards?
Real pictures. Only the k and v rows are kept, but the pictures show which frame is which.
The drawn frame is pushed
It gets noise at t = 0.05. Then one run through the 16 blocks reads the 15 frames on the shelves plus itself. That is 4,096 rows.
Its k and v join every shelf
Its k and v rows are joined to every shelf. The shelf is cut to its last 15 frames in the same step, so it never holds 16.
The oldest frame leaves
The oldest frame's 256 rows drop out. The shelves stay at 3,840 rows. The next key press starts again from pure noise.
Memory is always the last 15 frames. Anything older than that is gone. Idea from: CausVid (Yin et al., 2024), saved k and v. GameNGen (Valevski et al., 2024), a little noise on the past.
Live frame 15 A name for what we just followed, and where each part comes from.
Is this a diffusion transformer?
Yes: a DiT, adapted for video
The core is a DiT. It is a transformer that removes noise from an image cut into patches. It is told the noise level through adaLN-Zero.
From picture to patches to noise
A frame is 256 patches, each a row. The noise level and key set scale, shift and gate in every block. The frame is drawn from noise in steps.
The slope target and one noise level per frame
The output is a slope, noise − frame, so z moves on a straight line. Each frame has its own noise level: memory at 0.05, the new frame from 1 to 0.
Reading the past
The new frame reads 15 past frames from saved k and v. Rope tells attention how far back a frame is. Normalising q and k keeps the scores safe.
Yes. It is a DiT with the parts that make it a game model. Each row of the table names its source. Idea from: DiT (Peebles & Xie, 2022). Rectified Flow (Liu et al., 2022). Diffusion Forcing (Chen et al., 2024). CausVid (Yin et al., 2024). RoFormer (Su et al., 2021). ViT-22B (Dehghani et al., 2023).
Live frame 16 The last question of this part: what kind of model is this?
Is it a world model?
Yes: frames and a key in, the next frame out
A world model predicts what the world shows next, given what it showed before and the action taken. Here the 15 past frames and the key go in. The next frame comes out.
What a world model needs
It needs observations in, an action in, and the next observation out. Here these are the last 15 frames, the key, and the next 128 × 128 frame.
It runs on its own output
The drawn frame is pushed back into memory. The oldest frame leaves. When you play, the game engine is not running at all.
What it does not have
There is no hand-written rule and no stored bird position or score variable. What it knows about the game is in its learned numbers and in the last 15 frames and their keys. Anything older is gone.
Yes. It is a game world model. It is the same kind of model as GameNGen (Doom) and DIAMOND (Atari): a video diffusion model driven by the player's action. Idea from: World Models (Ha & Schmidhuber, 2018). GameNGen (Valevski et al., 2024). DIAMOND (Alonso et al., 2024).
Chapter E1 We follow real frame 326 and one pixel on the bird.
How does one frame turn into noise, one pixel at a time?
Two ends: frame and noise
On the left is real frame 326. On the right is eps, one random number for every pixel and colour. The ring marks the bird pixel we follow.
Mix them with t
The rule is mix(x, e, t) = (1 − t)·x + t·e. At t = 0.25 the picture is 75% frame and 25% noise. At t = 1 only noise is left.
One pixel, five numbers
Look at the red number of the ringed pixel. It is 0.694 in the frame and 1.228 in the noise. At t = 0.5 it is 0.961.
The five numbers sit on a line
Plot the pixel's value against t. The five dots fall on one straight line. In training, every pixel moves on a straight line like this.
One t for the whole frame
The same t is used for all 16,384 pixels and all three colours of the frame. Each frame gets its own t.
A noisy picture is a point on a straight line. The line starts at the real frame and ends at pure noise.
Idea from: Rectified Flow (Liu et al., 2022) and Flow Matching (Lipman et al., 2022), the straight line from frame to noise.
Chapter E2 eps is the noise end of every line on the last page.
What is eps, and why can it be above 1?
eps: one random number per pixel and colour
eps = torch.randn_like(x) draws a fresh random number for each pixel and colour. A frame has 49,152 of them. The picture shows a real eps for frame 326.
A bell curve, not a box
Most values sit near 0, but nothing stops a large one. About 32% of this eps lies outside −1 to 1. The largest is 4.1.
—of eps values outside −1 … 1
The frame lives in −1 … 1
Real pixels are bytes turned into the range −1 to 1. Only the frame is limited. So the bird pixel's eps of 1.228 is an ordinary noise value.
A new eps every time
Each frame in each training step gets new noise. Here is the same pixel in four different draws. It has a different number each time.
Why the bell curve
Play also starts from torch.randn noise. Training noise must be the same kind. Otherwise the model would learn to clean noise it never meets.
eps is just random noise, and it is kept. The right answer is built from this exact eps, so training stores it.
Idea from: Rectified Flow (Liu et al., 2022), noise drawn from a bell curve.
Chapter E3 The pixel's line from E1, now with the names from y = m·x + c.
How is v a slope, like the m in y = mx + c?
The same line, drawn with axes
The horizontal axis is the noise amount t. The vertical axis is the pixel's red value. In y = m·x + c the axis is called x, but here x means the frame. So we rename the axis t.
c: where the line starts
At t = 0 the line is at the real frame value. So c = x = 0.694. Here c is the line's start, not the condition row c of the blocks.
m: how steep the line is
Take the two ends: t = 0 gives 0.694 and t = 1 gives 1.228. Then (y_b − y_a) / (t_b − t_a) = (1.228 − 0.694) / (1 − 0) = 0.534. That is eps − x. Here v is this slope, not the value rows of attention.
Put in the slider
Move t. The noisy value is noisy = (eps − x)·t + x, which is m·t + c. At t = 0.60 it is 0.694 + 0.60 × 0.534 is about 1.015. Walking back down the line is the reverse.
Every number has its own line
Here are twelve other pixels. Each has its own start x and its own end eps, so its own slope. The model must give one slope per pixel and colour.
Why v looks like noise
The slope is eps − x, so it is mostly noise with the frame taken away. That is why a picture of v looks like noise.
v is literally m. It is the slope of each pixel's straight line between the frame and the noise.
Idea from: Rectified Flow (Liu et al., 2022) and Flow Matching (Lipman et al., 2022), the slope v = eps − x.
Chapter E4 Frame 326 at t = 0.60, with its real model output.
The model outputs 256 × 192 numbers. What are they, if not the image?
What goes in
The noisy frame 326 at t = 0.60 goes in. So do the lightly noised past frames, the key, and both noise levels. We draw only the noisy frame here.
What comes out: one slope per number
The head gives 256 squares of 192 numbers. Then squares go back into a 128 × 128 × 3 picture. Each number is a slope v, not a colour.
The answer sheet
The right answer for every number is eps − x. The two pictures match because training made them match. Each dot on the right is one real number.
—correlation, model v vs answer
Slopes give back the frame
Walk back down each line: x_hat = noisy − t·v. For the ringed pixel, 1.015 − 0.60 × 0.371 = 0.792. The real value is 0.694.
The noise comes back too
With the right slope, eps = noisy + (1 − t)·v gives 1.228. So the slope holds the noise. The model predicts the noise minus the frame.
Why the model is told t
Take the frame value 0.694. A noisy value of 1.015 at t = 0.60 needs v = 0.534. If the same noisy value arrived at t = 0.30, it would need v = 1.068. So t changes the right answer.
The walk back works
The mean miss against the real frame is 65.7 out of 255 for the noisy picture. After one step with v it is 0.16.
The output is a slope, not a picture. One subtraction, noisy − t·v, turns it into the frame.
Idea from: Rectified Flow (Liu et al., 2022) and Flow Matching (Lipman et al., 2022), the model predicts the slope.
Chapter E4b Frame 326 at t = 0.60: three subtractions on real pictures.
Why do the model's v and the answer eps − x look the same?
eps − x is noise plus the frame turned upside down
Add eps to the negated frame, and you get the answer. The noise is the big part, so the answer looks like noise. For the ringed pixel, 1.228 + (−0.694) = 0.534.
So the model does predict the noise
Look at the bottom of the answer. The dark band is the sandy ground, negated. The frame is in there. Mostly it is noise, and that is why the picture looks like noise.
Trained: v minus the answer is almost grey
Grey means zero. Magnified 3 times, the misses stay small. The ringed pixel misses by 0.371 − 0.534 = −0.163. The loss is 0.00004. Frame 326 is from a training game.
Brand-new: the whole answer is missed
A new model outputs 0 everywhere. Its difference is the whole answer, and its loss is 1.29. The two pictures match only because training made them match.
v looks like eps − x because training made it so. The picture holds noise and frame together.
Idea from: Rectified Flow (Liu et al., 2022).
Chapter E5 The same frame 326, graded.
What exactly is the loss, and what improves after backprop?
One number's miss
For the ringed pixel the model guessed v = 0.371. The right slope is 0.534. The miss is 0.163. Squared, it is 0.0266.
Average over every number
Do this for all 49,152 numbers of the frame. Take the mean. A real step averages 6,291,456 numbers into one.
Trained against brand-new
A brand-new model outputs 0 everywhere, so its miss is the whole slope. Its loss is 1.29. The trained model scores 0.00004 on this frame, which is from a training game.
—brand-new model
—trained model
Backprop moves every weight
loss.backward() gives each weight a gradient. Then opt.step() moves it against that gradient. One weight of a small model moved from 0 to −0.001. Adam's first step is about the learning rate, against the gradient's sign.
The loss falls as the weights improve
A small model trained on these frames fell from 1.29 to 0.35. On held-out frames, the stage 1 model scores about 0.0004.
Loss = mean squared miss of the slope. Backprop nudges all weights so that miss gets smaller. Nothing else is graded.
Idea from: Flow Matching (Lipman et al., 2022), the loss on v.
Chapter E5b The first idea most people have: give the noisy image, predict the clean image x.
Why not just predict the image x with a normal MSE?
Your idea: the model outputs x_hat
Give the model the noisy image and let it output the clean image x_hat. The loss is a normal MSE: mean((x_hat − x)²). This is a real method.
Ours: the model outputs v
The model outputs the slope v. We get the image with x_hat = noisy − t·v. The loss is mean((v − (eps − x))²). Both ways end in the same picture.
The two losses differ by exactly t²
The miss in x is t times the miss in v. So the loss in x is t² times ours. On frame 326, t = 0.60 and the ratio is 0.36.
—x-loss / v-loss, measured
What t² means for each frame
At t = 0.1 the x-loss counts a frame 0.01 times as much as the v-loss. Ours weighs lightly noised questions more.
Why we predict the slope
Play walks along the line in steps: z = z + (t_next − t)·v. A step needs exactly the slope. It is also the standard rectified-flow recipe.
Predicting x also works
A 2025 paper (JiT) shows that predicting x also works for pixel models. Here it is a choice of bookkeeping, not a different idea.
Same noisy image in, same clean picture out. Only the weight of each frame in the loss changes, by t².
Idea from: Rectified Flow (Liu et al., 2022), Flow Matching (Lipman et al., 2022); JiT (Li & He, 2025).
Chapter E6 What play does with the slope. Training does it differently.
How does play draw a new frame from pure noise?
Start from pure noise
There is no real frame to hide, so play starts from torch.randn. This picture sits at t = 1.0. Its mean miss against the real frame 326 is 94.8 out of 255.
Pass 1: the model gives a slope
The model sees the noise, the past 15 frames and the key. It outputs v1. The slope picture looks like noise, but it holds the way back.
Take half the walk
Play steps by t_next − t = 0.5 − 1.0, so z = z + (t_next − t)·v. Half of the noise is removed. Nobody adds noise. The miss falls to 48.2.
Pass 2: ask again at t = 0.5
The same model gets the half-clean picture and t = 0.5. It outputs v2. This is a new question, at a new noise level.
The last half of the walk
Step by 0 − 0.5 along v2. That gives the new frame. Its miss against the real frame is 0.7 out of 255.
Training asks once
Training never walks. It hides frame 326 at one random t, gets one slope, grades it against eps − x, and stops. Then it adjusts the weights.
Play walks 1.0 → 0.5 → 0.0 in two passes. Training asks one question per frame at one random t. Both use the same slope.
Idea from: Rectified Flow (Liu et al., 2022), walking back along the line in a few steps.
Chapter F1 Fail first: what goes wrong if the past is always perfect.
Why do we noise the past at all?
In play the past is drawn by the model
The left picture is a real frame. The middle one is the model's own redraw of it. They look the same, but the right picture shows the small differences, brightened 20 times.
Fail first: a perfect past in training
Suppose training only ever shows real frames as the past. Then play hands the model a past it has never seen. Each small error becomes the next frame's input. This sketch is an idea, not a measurement.
The fix: noise the past by a random amount
Each past frame gets noise t_c, drawn between 0 and 0.35. These are real pictures of one window. At 0.001 the frame is clean. At 0.311 it is clearly noisy.
Play's 0.05 is inside the range
Play always uses 0.05 for the past. Training saw every amount from 0 to 0.35, so 0.05 is familiar. Each dot is one real t_c from this window.
The model is told t_c
The model gets t_c with each past frame. So it can tell a clean past from a noisy one. The idea is that it then relies less on fine detail. This is not measured.
A slightly noisy past teaches the model to ignore small mistakes. Play's own drawings then do not push it off course.
Idea from: GameNGen (Valevski et al., 2024), noise augmentation on the past.
Chapter F2 One real window of 16 frames, made into two copies.
What are the two copies of one window, and what is each for?
One real window
Take 16 frames in a row from one game: frames 319 to 334. Under each frame is the key that produced it. Frame 326 has key 1, a tap.
Copy one: the clean copy, the past
Each frame is noised a little, by its own t_c from 0 to 0.35. This is the past as play will see it. It is never perfectly clean.
Copy two: the noisy copy, the questions
Each frame is noised by t_n, a random number from 0 to 1. Noisy position 9 is built from real position 9 itself, not from position 8. The exact eps is kept.
Follow one pixel at position 9
The clean value uses t_c = 0.311. The noisy value uses t_n = 0.794. The noise of the clean copy is thrown away. The eps of the noisy copy is kept for the answer.
The code, loss.py:13-24
The two lines with mix are the same formula. They differ in the job. Clean is the past. Noisy is the question.
Both noise levels go into the model
The call passes clean, t_c, noisy and t_n. The model needs t_n because the right slope changes with t. It needs t_c to know how far to trust the past.
Same mix, two jobs. The clean copy is the past it reads. The noisy copy is the set of questions it must answer.
Idea from: Self Forcing (Huang et al., 2025), teacher forcing; Diffusion Forcing (Chen et al., 2024); SD3 (Esser et al., 2024), t.
Chapter F2b Two noise numbers, two kinds of frame, training and play.
Which noise number belongs to which frame, in training and in play?
Training, the past: t_c from 0 to 0.35
The past frames get t_c = rand * 0.35. The blue band shows the range. The dots are the 16 real t_c values of the window on the last page.
Play, the past: always 0.05
When play pushes a frame, it uses 0.05. That sits inside the blue band. So the model has met this noise level in training.
Training, the frame being drawn: t_n from 0 to 1
The noisy copy gets t_n = sigmoid(randn). Most values are medium. Half of them fall between 0.34 and 0.66. Only 1.4% are below 0.1.
Play, the frame being drawn: 1.0, 0.5, 0.0
Play starts at pure noise and removes noise in two passes. Nobody adds noise at 0.5. The picture simply is at 0.5 after pass 1.
Do not mix the rows
The 0.60 on earlier pages is one dot in the bottom left box. The range 0 to 0.35 belongs only to the past. The range 0 to 1 belongs only to the frame being drawn.
Past frames get a little noise. The frame being drawn gets any amount. Training covers every level that play will use.
Idea from: Diffusion Forcing (Chen et al., 2024); GameNGen (Valevski et al., 2024); SD3 (Esser et al., 2024).
Chapter F3 32 frames in one pass: 16 clean, then 16 noisy.
Which frames may each frame read?
A 32 × 32 grid of frames
Each row is a frame that reads. Each column is a frame that may be read. The order is C0 to C15, then N0 to N15. Grey means blocked.
Clean frame i reads clean 0 … i
The blue triangle is the clean copy. It reads itself and the clean frames before it. This is exactly what a push does in play.
Noisy frame i reads clean 0 … i−1, and itself
Each orange row reads the clean frames before it. It also reads its own noisy frame. This is exactly what a draw does in play.
Follow noisy position 9
Noisy 9 reads clean 0 to 8 and itself. It must not read clean 9. That is the same frame, only lightly noised, so it would give away the answer.
272 of 1,024 pairs are allowed
That is 26.6% of the grid. The rule is the function can_read in world.py:11-20. The other 73.4% of pairs get zero share.
272of 1,024 frame pairs
26.6%allowed
Each noisy frame sees what play would show it. Only a clean past, plus itself. Nothing from its own answer.
Idea from: Self Forcing (Huang et al., 2025), its Teacher Forcing baseline; Causal Forcing, clean and noisy copies with one mask.
Chapter F4 The whole training pass, with the shape after each step.
How does one pass ask 16 questions at once?
32 frames become 8,192 rows
Clean C0 to C15 and noisy N0 to N15 sit in one list. Each frame has 256 squares, so each frame is 256 rows of 768 numbers. One window is 32 × 256 = 8,192 rows.
16 blocks, with the can_read rule
All 8,192 rows go through the 16 blocks together. The grid from the last page decides who reads whom. The shape does not change.
Keep only the noisy half
The model uses x[:, T:], the last 16 frames. The clean half only served as memory. It gets no output and no grade. That is world.py:84-96.
16questions per window
128per step, 8 windows
Slopes out, then the loss
The head gives 256 × 192 numbers per frame. They fold into a 3 × 128 × 128 picture of slopes. Each one is graded against eps − x.
Each question has its own t
Every noisy frame has its own t_n. So the 16 questions ask about 16 different noise levels in one pass. Play draws at two levels, 1.0 and 0.5.
One pass, 16 questions, one grade per noisy frame. Training asks each frame once. It never walks like play does.
Idea from: Self Forcing (Huang et al., 2025), its Teacher Forcing baseline, all frames in one pass.
Chapter G1 The loss is one number. This page shows what happens to the weights next.
After the loss, how does one update change the model?
A batch becomes one number
In the real run one batch is 8 windows of 16 frames. The model guesses a slope for every pixel. The loss averages all the misses into one number.
The numbers on steps 1 to 4 come from a small demo model: 758,848 weights, one 4-frame window.
1.2907loss of the small demo model, step 0
backward: one gradient for each weight
First zero_grad forgets the last step. Then loss.backward() gives every weight its own gradient. A gradient says which way the loss moves if that weight goes up.
Follow one weight of the demo model: out.weight[0,0]. Its gradient is +0.0000469, so raising it would raise the loss.
Clip: keep the push small
All gradients together have one length. If that length is above 1.0, they are all shrunk until it is exactly 1.0. In stage 1, 2 of the 296 logged values went above the line. We log every 100th step.
AdamW moves every weight a little
Each weight moves against its gradient. Our demo weight goes from +0.0000000 to −0.0009998. The demo learning rate was 0.001 with no warmup, so the first move is about that size. The real run uses 0.0002. AdamW uses betas 0.9 and 0.95 and weight decay 0.01.
758,848demo weights moved at once (the real model: 172,518,848)
Warmup: tiny steps first
The first gradients are junk, so the learning rate starts at zero. It climbs in a straight line to 0.0002 over 1,000 steps. Then it stays there.
Repeat, and the loss falls
Next window, next noise, same four moves. The demo model goes from 1.29 to 0.35 in 1,500 steps. The real model reaches 0.00055 at the last logged step, 29,500.
Two one-line speedups
Under autocast the maths runs in bf16, which is faster on the GPU. And torch.compile wraps the loss once. Neither changes the idea above.
The next step after backprop is the update. Gradients say which way, clipping keeps the push small, and AdamW moves every weight. Repeat that and the loss falls. Idea from: AdamW (Loshchilov & Hutter, 2017); gradient clipping by total norm (Pascanu et al., 2013); learning-rate warmup (Goyal et al., 2017).
Chapter G2 The EMA is part of our own run. It is not another model.
How is the shadow copy of the model made?
Copy the model once, at step 0
In stage 1 both copies start from the same random weights, so nothing comes from a finished model. The real numbers on this page come from a small demo model of 758,848 weights. The copy gets no gradients, so it never learns by itself.
After every step, the shadow follows
The model takes its AdamW step. Then each shadow weight moves a small part of the way toward the model weight. The rule is ema = d * ema + (1 - d) * model.
Follow one demo weight at step 400
The shadow was at the old value. The model weight has moved on. The shadow takes only 2.2% of the distance and stays close to where it was.
The share d changes with the step
At step 1 the shadow moves 82% of the way, so it can leave the random start. In the 600-step demo, d only reaches 0.985. In the real run d reaches 0.9995 near step 18,000.
0.9995d from about step 18,000 on
Six hundred steps of one demo weight
The grey line is the model weight. It wobbles because each step uses one random batch. The blue shadow follows a little behind and stays smooth.
Play and the checks use the shadow
At the end of training we save both copies. The EMA file is the one we load to play the game. The next page shows why.
The EMA is a running average of our own weights. It is one extra copy, pulled a little toward the model after every step. Idea from: EMA of the weights (Polyak & Juditsky, 1992, averaging), used for diffusion sampling in DDPM (Ho et al., 2020).
Chapter G3 The shadow copy costs one extra copy. Here is what it buys.
Why do we need the shadow copy at all?
The model weights jitter
Every AdamW step is pushed by one random batch. So the weights keep jumping around a good spot. Look at the second half of a small demo run: 3,000 steps of the 758,848-weight model. The grey model loss on 64 held-out windows zig-zags.
The shadow averages the jitter away
The blue shadow copy goes smoothly through the middle of the zig-zag. Here it beat the model at 24 of the 31 checks in this zoom. In this short run d is 0.997, so it averages about the last 334 steps. In the real run d is 0.9995, about 2,000 steps.
Early on, the shadow is worse
On the whole run the two lines look the same. But from step 50 to 500 the shadow lags behind a model that still improves fast.
After step 1,550 it is a little better. Its loss jumps about two thirds as much between checks.
0.00044average jump of the shadow loss (model: 0.00068)
The real run: the gap is big
On the small run the gain is tiny, 0.4082 against 0.4088. On our full stage-1 run it is clear. At step 28,000 the model scores 0.00074 and the shadow scores 0.00039, about half.
1.9xlower held-out loss with the shadow copy
So we play with the shadow
The loss we train on is the model's. The weights we keep, play and check are the shadow's. The shadow never gets gradients, so it costs one extra copy.
The shadow copy is the steadier model. It lags early, but at step 28,000 of the real run its held-out loss is about half of the model's. Idea from: EMA of the weights (Polyak & Juditsky, 1992, averaging), used for diffusion sampling in DDPM (Ho et al., 2020).
Chapter G4 Fail first. We run the same loop on a tiny model on the laptop.
What happens when the model is too small?
A model 227 times smaller
The small model has 2 blocks and width 128. That is 758,848 learned numbers. The real model has 172,518,848, so the small one is 0.44% of its size.
758,848learned numbers, against 172,518,848
Same loop, 1,500 steps
We train it on one window of 4 real frames from a training game, again and again. The loss falls fast from 1.29 and then stalls near 0.35.
One fixed question
We hide frame 326 behind noise at level 0.6. Then we ask the model for the clean frame. The left picture is the real frame, and the second is the noisy one it sees.
The small model draws noise
At step 0 it outputs zero, so we see the noisy frame back. At step 1,500 the scene shows, but it is still covered in noise. The miss is the average gap per pixel and colour, 33 on a 0 to 255 scale.
33 / 255average pixel miss after 1,500 steps
The real model on the same question
Our final stage-3 model answers with a clean frame. Its miss is 0.20 out of 255. Its loss on this question is 0.00013, against 0.35 for the small one.
0.20 / 255average pixel miss, stage-3 model
The small model learns the loop and stalls. This is not a controlled size test. The big model trained for tens of thousands of steps on many games, and this frame is from a training game. Held-out loss is on page G5. Idea from: Rectified Flow (Liu et al., 2022), the slope the small model must learn; the size test is standard practice.
Chapter G5 The same loop, with the real model, on a real GPU.
How does the loss of our stage 1 fall on the GPU?
Stage 1 on GPU 7
We trained the full 172.5M model from random weights on one H100. It ran for 121 minutes at about 0.246 seconds per step. That gave 29,525 steps, using 56.1 GB of memory.
29,525steps in 121 minutes
The training loss falls more than 3 orders
The loss starts at 1.28, the same as the small model. At the last logged step it is 0.00055, about 2,300 times lower. The line wobbles because every step uses a new batch.
The held-out loss falls smoothly
Every 2,000 steps we score windows from games the model never trained on. These dots fall smoothly. They sit on or a little above the training loss, which is what we expect for games it never saw.
The shadow copy is lower again
The dashed line is the shadow copy on the same windows. At step 28,000 the model scores 0.00074 and the shadow scores 0.00039.
0.00039held-out loss of the shadow copy, step 28,000
Stage 1 trains smoothly. The loss falls from 1.28 to 0.00055, and the shadow copy ends at about half the model's held-out loss. Play checks come next. Idea: standard practice (train from scratch, score held-out games, keep the EMA copy).
Chapter H1 Live play must draw a frame fast. Here is the cost of the first idea.
Why not read the whole past again for every new frame?
First idea: re-read all 15 past frames
For every new frame, push the 15 past frames through the 16 blocks again. Each frame is 256 rows. So that is 15 x 256 rows, plus 2 passes of 256 rows for the new frame.
4,352rows through the 16 blocks, per new frame
Better: keep the past, do only the new work
A finished frame never changes. So we save its k and v once, on the shelf. Then each new frame costs 2 passes to draw and 1 pass to save: 3 x 256 rows.
768rows per new frame, with the shelf
Do the multiply: what is on the shelf
A frame is 128 x 128 pixels. Squares of 8 pixels give 16 x 16 = 256 squares, so 256 rows. 15 frames give 15 x 256 = 3,840 rows.
Every block saves k and v for those rows, each 768 numbers wide. The shelf is the kept k and v of all 16 blocks. That is 16 x 3,840 x 768 for k, and the same again for v.
The past is read in one place only
Each block's 256 new rows read 3,840 rows from that block's part of the shelf, plus their own 256. That is 4,096 rows, inside attention in every block. No past frame is ever added to the noise as pixels.
The shelf means 5.7 times fewer rows through the blocks. It holds 94,371,840 numbers, and the new frame reads it inside attention. Idea from: CausVid (Yin et al., 2024), keep k and v of past frames (KV cache) for frame-by-frame video.
Chapter H2 The shelf holds the past. Two moves use it: push and draw.
How do push and draw fill and read the shelf?
The shelf is a grid
The shelf is the kept k and v of all 16 blocks. Draw it as 16 layers, one per block, and 15 columns, one per past frame. Each cell holds that block's k and v for the 256 rows of that frame. One block's part of the shelf has k and v of shape [B, 12, 3840, 64]. Blocks are counted from 1.
Push: save a finished frame
First we add 0.05 noise to the pixels of the frame. Then one pass runs through the 16 blocks. Each block writes its k and v into a new column, and the oldest column drops out.
The noise goes on the picture, never on k or v. The head output of this pass is thrown away.
Draw: make the next frame
We start from pure noise. Pass 1 goes from t = 1.0 to 0.5, and pass 2 from 0.5 to 0.0. Both passes read the shelf. The shelf does not change during a draw.
One real draw, in pictures
Pass 1 asks the model for a slope. We take half a step back along it: z minus 0.5 times v. Pass 2 does the same from half noise.
No new noise is added while drawing. The pixel miss falls from 94.8 to 48.0 to 0.72 out of 255. In stage 1 there is one pass and the model never draws. In stages 2 and 3 the EMA copy redraws two frames of each window.
Three passes for each frame
The finished frame has not run through the blocks yet. So one more pass makes its k and v. Pass 2 only saw a half-noisy picture.
In training the past gets a random noise level from 0 to 0.35. Our 0.05 sits inside that range. Streaming costs 3 passes of 256 rows.
The model holds no score and no crash rule
It draws the score digit as pixels, like the pipes. A separate pixel check ends the game. The bird counts as found with 3 orange pixels, and 3 missed frames in a row end the run. The play checks draw with 4 passes, these pages show 2.
The next part follows one real look-up through the shelf with real numbers.
Push writes the shelf. Draw reads it. Each frame uses 2 draw passes and 1 push pass, all of them 256 rows through 16 blocks. Idea from: CausVid (Yin et al., 2024), KV cache; GameNGen (Valevski et al., 2024), noise on the past.
Chapter H3 Rotating q and k by position, and why q·k depends only on m − n, is on page C3.
Why must a saved key be un-rotated, and what is pos=None?
m and n in the play loop
The shelf holds the last 15 frames. The oldest is at position 0 and the newest at 14. The frame being drawn is at 15, as streamer.py:24 says.
Each frame has 256 squares, so each position is used for 256 keys. Click any frame to follow it.
Slide the window: every position drops by one
Move the slider by one. The oldest frame leaves, and every other frame moves down by one position. Frame 318 goes from position 7 to 6, 5, 4 and so on.
Save the key rotated, or un-rotated?
Row B saves k already rotated, once, when the frame was saved. It was the frame in hand then, so it was rotated by 15. It is never rotated again.
Row A saves k plain and rotates it at each use. So m − n is always the true number of frames back.
That is why pos=None
Inside the block, block.py:35-36 would rotate only the 256 new keys. With pos=None the block skips it and hands k back plain at line 41.
Then attention, called at line 37, glues the shelf on and rotates all 4,096 keys with pos_all.
One whole pass, with real shapes
This is _pass for one frame, batch size 1. The yellow rows are the ones that matter for this trap. Every pass, 4,096 keys are rotated in each block.
94.4 Mnumbers saved on the shelf
A saved key must not carry a position. The window slides, so the position must be counted again at every pass. Idea from: RoFormer (Su et al., 2021), RoPE; CausVid (Yin et al., 2024), the KV cache.
Chapter H4 We built the wrong version on purpose and played both on real frames.
Can one drawn frame show the trap?
Draw one frame both ways
We push 15 real frames into an empty shelf. Then we draw frame 313 with plain keys, and again with keys rotated at push. Both miss the real frame by about 0.7 out of 255.
The shelf was still filling, so the rotated keys carry 0 to 14. Those are the correct offsets for this first draw. The two designs are the same maths here, so one frame cannot tell them apart.
1.24miss, plain keys, out of 255
1.24miss, rotated at push
Where does one real frame look?
The bars show how much the frame being drawn looks at each saved frame. It is our stage-3 model on real frames, drawing frame 326 after 28 pushes. We look at block 9 of 16, counted from 1.
Plain keys: it looks most at itself and the newest frames, then less and less. We read this as how it sees the bird's speed. We did not test that.
Rotated at push: every frame looks the same
The window has slid by now. Almost every saved key was rotated by 15 when it was saved, and the two oldest by 13 and 14. So they look about 0 frames back. The bars are flat, and old and new look the same.
Draw 30 frames in a row
Now each drawn frame goes back on the shelf, as in real play. Plain keys follow the real game. Rotated keys draw a bird that does not move. The window slides, and that is the whole trap.
The bird height tells the truth
The line is the bird's height in each frame. Plain keys stay on the real line. Rotated keys stay at row 59 to 60 for all 30 frames.
One frame cannot see the bug. Thirty frames can. A test for memory must run many frames and watch the bird. Idea from: RoFormer (Su et al., 2021), RoPE. The 30-frame test is ours.
The memory, 1 of 3 Page H3 explains why keys stay plain. This page only counts. Blocks are counted 1 to 16; the code counts 0 to 15.
How many numbers does the memory hold?
One frame is 256 rows
A frame is 128 × 128 pixels. We cut it into squares of 8 × 8 pixels, 16 across and 16 down. That gives 16 × 16 = 256 squares. Each square becomes one row of 768 numbers.
256rows in one frame
15 frames are 3,840 rows
The memory holds the last 15 frames. Each frame is 256 rows, and the frames sit one after the other. So 15 × 256 = 3,840 rows. We call this the past table.
3,840rows in the past table
One block saves two tables
The 15 frames go through the 16 blocks in one batch. Live play pushes them one at a time. It makes the same v, and the same k before the turn by position. Each block keeps k and v, each 3,840 × 768, which is 2,949,120 numbers.
16 blocks keep 16 shelves
Every block keeps its own pair, so there are 16 shelves. For k alone, 16 × 3,840 × 768 = 47,185,920 numbers. The tables for v have the same size.
47,185,920numbers in k
47,185,920numbers in v
Together: 94,371,840 numbers
Add k and v: 47,185,920 + 47,185,920 = 94,371,840. Count it another way: 15 frames in 16 blocks make 240 saved pieces. Each piece is a k and a v of 256 × 768, so 480 tables.
94,371,840numbers in the whole memory
A block reads 4,096 rows
The new frame is only 256 rows. Block n reads its own 3,840 saved rows and these 256 new rows. So 3,840 + 256 = 4,096 rows, each of 768 numbers.
4,096rows one block reads
The memory is 16 × 2 × 3,840 × 768 = 94,371,840 numbers. The next page shows where each saved row comes from. Idea from: CausVid (Yin et al., 2024), saved k and v reused frame by frame.
The memory, 2 of 3 Pages H2 and H3 explain the shelf and why keys stay plain. Here we see where the shelves come from. Blocks are counted 1 to 16.
Where do the saved rows come from?
Run 1: 15 past frames, one batch
These pictures read the 15 past frames in one batch. Each frame gets a little noise (t = 0.05) and is cut into 256 squares. 15 × 256 = 3,840 rows. Live play pushes one frame at a time and builds the same tables.
3,840rows in the past table
Block 1 saves its k and v
The 3,840 rows enter block 1. Block 1 makes q, k and v for every row, 3,840 rows each. It puts k and v on shelf 1. That is what "saved" means. These k pictures are turned by position. Live play stores k plain (page H3).
Block 2 saves its own pair
Block 1 also runs attention and its MLP on the past table, so the table changes. The changed table enters block 2. Block 2 makes its own k and v, with its own weights, for shelf 2.
All 16 blocks do the same
Every block fills one shelf. So 16 shelves × 15 frames = 240 saved pieces, each 256 rows. Shelf n holds the k and v that block n made from the table entering block n. The table after block 16 is never used.
16shelves
3,840rows on each
Run 2: the new frame reads shelf 1
The new frame is a table of only 256 rows. Here it is the half-noisy frame (t = 0.5, key 0) that enters draw pass 2. Block 1 makes its own q, k and v. K is shelf 1's k with the new k below it, 4,096 rows, and V is built the same way. This stacking is the look-up.
Every block reads its own shelf
Block 2 reads shelf 2, block 9 reads shelf 9, and so on. Each shelf was made by the same block in run 1. So the past enters the new frame 16 times, once in each block.
The new frame joins the shelves
After block 16, the head turns the 256 rows into pixels: the new frame. Live play then pushes it at once, in one pass at t = 0.05. Its 256 rows join every shelf, and the oldest frame's 256 rows leave.
The past enters only through the shelves. Each frame fills its band once, by a push. Run 2 reads them in every block. Idea from: CausVid (Yin et al., 2024), saved k and v reused frame by frame.
The memory, 3 of 3 Real numbers: the bird's square, block 9, head 12 (counted from 1; code index 8 and 11). The frame is half noise (t = 0.5, key 0), as it enters draw pass 2.
Where are the past frames read?
Two separate tables
The 15 past frames make a tall blue table of 3,840 rows. The new frame makes a short red table of 256 rows. It is half noise (t = 0.5). Both are drawn to scale. Nothing from the past is added to its pixels.
Earlier, block 9 saved k and v
These tables come from one batch read of the 15 frames. Live play pushes them one at a time. Block 9 kept k for matching and v for content, 3,840 rows each. Its k is turned by position here; play stores it plain (page H3).
Now: new q, k, v, then stack
The new table reaches block 9. Its 256 rows make q (the question of each square), k and v. The saved rows go on top of the new rows, so K and V have 4,096 rows. This stacking is the only place the past enters.
3,840saved rows
256new rows
One square asks: the bird
Take row 132 of q, the bird's square. Its 64 numbers are compared with all 4,096 rows of K, and each score is divided by 8. Softmax turns the scores into shares that add up to 1.
92.1%of the shares on past rows
7.9%on the new frame
Where those shares sit
Each share belongs to one square of one frame. Drawn back on the pictures, the biggest sit on past frames 15 and 14. They are at square 131, next to the bird's square 132. Past frame 15 is the newest of the 15.
37.4%past frame 15, square 131
17.3%past frame 14, square 131
Take that much of each v row
Each of the 4,096 v rows has 64 numbers. Multiply each row by its share, then add up all the rows. The five biggest parts are shown. The sum is the bird's new 64 numbers for this head.
The mix is added to the bird's row
All 12 heads do this. Their 12 × 64 results join into 768 numbers, pass through proj, and are scaled by the gate. The result is added to the bird's row, so the new table now carries the past. Block 10 repeats this with its own shelf.
The look-up is one line: out = shares · V. The past frames never go back in as pictures. Their saved rows are read inside every block. Idea from: Transformer (Vaswani et al., 2017), attention shares; CausVid (Yin et al., 2024), saved k and v.
Chapter J1 Training is done. Now we need to know if the world works.
How do we know the world works, not only that the loss is low?
The loss looks perfect
After stage 1 the held-out loss is tiny. But the loss always reads a real past. In play the model reads its own drawings. So we let it play and measure.
0.00039loss, EMA copy
6%bird found, frames 101-200
Check A: where is the bird?
We find the bird as the orange pixels and take their average row. The model draws 300 frames with the real keys. Each frame, we compare its bird row with the real bird row.
Check B: does a pipe appear?
We pick 256 moments where a new pipe enters on the right. The model starts 4 frames early and draws 5 frames. We count green pixels in the yellow strip. A pass needs 30% of the real green. A short clip may not show a failure, so we test many moments. Stage 1 and stage 3 are two models of ours, explained later.
Check C: can it play alone?
In a free dream an auto-pilot taps toward the gap, so no real key helps. The dream runs all 1,000 frames. The loss frame is the first time the bird is missing for 3 frames. A wall is a pipe with no gap of 12 sky rows.
Read each number against the real game
A number alone means little. The real game is the target. By definition its bird miss is 0 and its pipe score is 100%. Survival is capped at 1,000, so more is better.
A low loss is not a working world. Only letting the model play on its own drawings shows if the bird and the pipes survive. Idea: ours (the three play checks).
Chapter J2 The same three checks, run on stage 1 at three points in its training.
Is stage 1 good, and what does it get wrong?
Five numbers, three checkpoints
We saved stage 1 at steps 6,604, 14,169 and 29,525. Each bar is one checkpoint, with the first in grey. Green means better than the checkpoint before. Red means worse. Each bar is one run, so small changes may be noise.
Pipes and bird position improve
Later checkpoints have fewer wall frames and a smaller bird miss. At 6,604 the bird misses by 2.02 px. At 29,525 it misses by 0.135 px. But the bird is found less and less.
See it in free dreams
These are real dreams of the final checkpoint. Green frames still show the bird. The dream runs on after the bird is lost, with pipes only. The loss never showed this.
Two things stay wrong
The bird is found in only 6% of frames 101-200. A new pipe appears after 4 own frames in 84.4% of cases, and the real game gives 100%.
6%bird found
84.4%new pipe appears
Stage 1 beats its early checkpoints on pipes and walls, but it loses the bird and misses about 1 new pipe in 6. Check more than the last checkpoint: the early one found the bird more often, but drew it badly. Idea from: GameNGen (Valevski et al., 2024), run the model on its own frames and look at the drift.
Chapter K1 Stage 1 loses the bird in long play. Here is why.
Why does the bird get lost when the model plays alone?
In training the past is always real
Each training window has 16 real frames. Each frame is asked from the real frames before it, lightly noised. It never reads a picture that it drew itself.
In play the past is its own drawing
At the start the 15 past frames are real. But each frame it draws becomes part of the past. After 15 frames, the whole past is its own work.
One drawing looks the same as the real frame
Real frame 8 and the model's drawing look alike. The difference, times 8, shows only a few bright pixels. The mean miss is under one level out of 255.
0.47mean miss, out of 255
Small misses pile up
The next frame is drawn from a slightly wrong past. Its own miss adds to the old one. So the bird height miss grows with every 100 frames of play.
0.70 to 6.2px, step 14,169
The model never practised on its own mistakes. Stage 2 fixes that: it puts the model's own drawings into the past during training. Idea from: GameNGen (Valevski et al., 2024), the model drifts when it plays on its own frames.
Chapter K2 Stage 1 reads a real past. Stage 2 lets the model read its own drawings.
What exactly does self-forcing change in training?
Start from one real window
This is one real window of 16 frames with its keys. Stage 1 uses it as it is. Stage 2 begins with the same window.
The EMA redraws two frames
We pick s0 between 1 and 14. Here s0 is 8. The EMA reads frames before 8 and draws frame 8. Then it draws frame 9. It uses 4 passes and no gradient.
Only the clean copy changes
The clean copy is the past. Its frames 8 and 9 are now the EMA's drawings. The noisy copy and the targets stay real. So the model must still draw the real frame.
Two code changes
In loss.py the clean copy reads xc instead of x. In loop.py the EMA fills xc. The EMA only draws the past. The target stays the real frame, so nothing is copied from the EMA.
The settings change too
Stage 2 starts from the stage-1 EMA, not from random numbers. The learning rate halves and the warmup is shorter. The bird counts five times in the loss.
Same loss, same real targets, a different past. The model now learns to draw correctly even when its past is slightly wrong. Idea from: Self Forcing (Huang et al., 2025), train on the model's own drawings as the past.
Chapter K3 The loss averages over every pixel. The bird covers almost none of them.
Why does the bird need five times the weight?
The bird is a speck
This frame has 16,384 pixels. The bird is only 14 orange pixels, under 0.1% of the frame. A model can miss the bird and still have a tiny loss.
Find the bird pixels
We zoom into 28 by 28 pixels around the bird. Each square is one pixel with its real colour. The black squares pass the orange test from rollout.py.
Grow the mask by 3 pixels
The bird edge and the sky next to it matter too. So bird_mask grows the orange pixels by 3 pixels each way. The mask now covers 141 pixels.
Count the bird five times
Each miss is multiplied by 1 plus 4 times the mask. So a miss near the bird counts five times. The bird share of the loss rises from 0.86% to about 4%.
0.86%of the pixels
5xweight on them
The weight does not change the targets. It only makes the bird's misses louder, so the model stops ignoring the bird. Idea: standard practice (weighted loss).
Chapter K4 Stage 2 ran for 120 minutes. We checked four checkpoints, not only the last.
Did stage 2 fix the bird, and what did it cost?
Five numbers on five checkpoints
Grey is our stage 1. The next four bars are stage 2 after 2,581, 5,501, 11,348 and 17,181 steps. Green means better than stage 1. Red means worse.
The final checkpoint, in one line
Survival goes from 118 to 196 frames. Wall frames drop from 0.84% to 0.006%. The bird is found in 18% of frames 101-200, up from 6%. Bird miss counts only frames where both birds are found.
118 to 196survival, frames
6% to 18%bird found
The last checkpoint is not always the best
Survival is highest at step 11,348 with 273 frames. Walls are lowest at the end. With one run and 32 dreams, small gaps may be noise.
The dreams are real
In these free dreams the bird stays for the frames shown. The four saved dreams last 949, 300, 895 and 41 frames. The loss would never tell us this.
The cost: new pipes dip
New pipes appear 80% of the time, down from 84%. Stage 2 is lower at every start: by 1.1, 5.1 and 4.3 points. Stage 3 targets this dip.
84% to 80%new pipe after 4 own frames
Stage 2 helps the bird and the walls, and costs new pipes. It gains survival and bird, and it loses 4 points on pipe births. Idea from: Self Forcing (Huang et al., 2025), train on the model's own drawings.
Chapter K5 Stage 2 lost some new pipes. Stage 3 practises exactly that moment.
How does stage 3 practise the moment a pipe is born?
A birth window
A birth window is 16 real frames. Its last frame is the first frame with a new pipe at the right edge. We find 7,162 of them in the training games.
The test is plain pixel counting
In frame 14 the right edge has no green. In frame 15 it has green. Check B picks its moments with the same rule.
0green px, frame 14
89green px, frame 15
Show them 25% of the time
Birth windows are 2.60% of all windows. Stage 3 uses them in 25% of the batches. So the model sees a pipe being born about 10 times more often.
Redraw the two frames before the birth
In a birth window s0 is 13. The EMA redraws frames 13 and 14. Then the model must draw the real frame 15, with its new pipe, from that past.
Four changed lines
The loop chooses a pool of windows with probability 0.25. In birth windows it sets s0 to 13. The loss and the bird weight stay as in stage 2.
Stage 3 is stage 2 with one change: it practises where it fails. After two of its own frames, the new pipe must still appear. Idea: ours (hard-example sampling, standard practice).
Chapter K6 The five numbers for stage 1, stage 2 and stage 3, side by side.
What did stages 2 and 3 win, and what is still open?
Stage 1 is the base
These are the five numbers of stage 1. Every other cell is coloured against them. Green means better than stage 1. Red means worse.
Stage 2: walls, survival and bird
Stage 2 cuts wall frames to 0.006% and lifts survival to 195.5. The bird miss and bird found also improve. Bird miss counts only frames where both birds are found. But new pipes drop from 84.4% to 80.1%.
Stage 3: the pipes come back
New pipes appear 97.3% of the time. Survival is 201.5 frames, the best of the three final models. Stage 2 at step 11,348 reached 273. Against stage 2, walls rise to 0.12% and bird found falls to 16%.
97.3%new pipe, was 84.4%
201.5survival, was 118
The dreams, once more
These are stage 3 dreams. In dream 1 the bird is lost at frame 80. In dream 2 it stays past frame 500. The median over 32 dreams is 201.5 frames.
Stage 3 is best of the three final models on pipes (97.3%) and survival (201.5). It trades some walls and bird found against stage 2, and the weights are open to check. Idea from: Self Forcing (Huang et al., 2025) for stage 2; the birth windows of stage 3 are ours.