Flappy Dream
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 idea A diagram for each idea one question per page, one drawing answers it You step through it press → or click a step on the right; the drawing builds one step at a time
172.5Mlearned numbers
300,153real frames to learn from
3training stages
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.

  1. 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.

  2. Your key

    You press TAP or nothing. That one bit goes in with the new frame. It is the only thing you control.

  3. Start from pure noise

    The new frame starts as random static. The transformer must turn this static into the next frame.

  4. 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
  5. 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?

  1. 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
  2. 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.

  3. 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.

  4. 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
  5. 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.

  6. 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.

  1. 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.

  2. 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.

  3. 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%)
  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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
  4. 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.

  5. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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
  4. 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?

  1. 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.

  2. 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.

  3. 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
  4. 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).

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

    768numbers per token: the model's width
    148,224learned numbers: 147,456 weights + 768 biases
  7. This is not a VQ-VAE

    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.

  8. 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.

  9. 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?

  1. 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
  2. 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.

  3. 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.

  4. 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?

  1. 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.

  2. 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.

  3. 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].

  4. 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.

  5. 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.

  6. 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
  7. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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
  6. 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?

  1. 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.

  2. 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
  3. 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.

  4. 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.

  5. 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.

  6. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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
  6. 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.

  7. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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
  5. 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.

  6. 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.

  7. 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?

  1. 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.

  2. 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
  3. 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.

  4. 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.

  5. 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.

  6. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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)
  5. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  1. 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).

  2. 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.

  3. The change

    The product is the change this half makes. Only a few columns are big, because only a few g1 numbers are big.

  4. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. 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.

  2. 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.

  3. What the attention half changed

    This map shows how much the attention half changed each square. Darker blue means a bigger change.

  4. What the MLP half changed

    The same for the MLP half, in green. Compare the two columns block by block.

  5. 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.

  6. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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 − real Idea 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.

  1. 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.

  2. LayerNorm each row

    Each row gets its own mean 0 and spread 1, as in every block. No row looks at another row.

  3. 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.

  4. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. 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.

  2. 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.

  3. 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?

  1. 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.

  2. 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.

  3. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  1. 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.

  2. 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.

  3. The output is thrown away

    The head runs during a push, but its output is not used. Only the k and v rows matter.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. 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.

  2. 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.

  3. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. 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.

  2. 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.

  3. 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.

  1. 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.

  2. 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.

  3. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  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.

  2. 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.

  3. 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.

  4. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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
  5. 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.

  1. 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.

  2. 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.

  3. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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?

  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.

  2. 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
  3. 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.

  4. 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.

  5. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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?

  1. 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.

  2. 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.

  3. 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
  4. 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.

  5. 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.

  6. 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.

  7. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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?

  1. 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.

  2. 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.

  3. 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
  4. 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.

  5. 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?

  1. 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.

  2. 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.

  3. 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
  4. 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.

  5. 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.

  6. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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?

  1. 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.

  2. 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.

  3. 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
  4. 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.

  5. 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?

  1. 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
  2. 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.

  3. 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.

  4. 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)
  5. 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.

  6. 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.

  7. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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
  5. 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.

  6. 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?

  1. 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.

  2. 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.

  3. 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)
  4. 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
  5. 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?

  1. 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
  2. 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.

  3. 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.

  4. 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
  5. 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?

  1. 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
  2. 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.

  3. 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.

  4. 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?

  1. 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
  2. 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
  3. 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.

  4. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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?

  1. 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
  2. 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.

  3. 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.

  4. 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.

  5. 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?

  1. 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
  2. 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
  3. 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.

  4. 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
  5. 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
  6. 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?

  1. 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
  2. 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).

  3. 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.

  4. 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
  5. 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.

  6. 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.

  7. 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?

  1. 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.

  2. 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).

  3. 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
  4. 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
  5. 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
  6. 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.

  7. 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?

  1. 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
  2. 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.

  3. 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.

  4. 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.

  5. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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?

  1. 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.

  2. 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.

  3. 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
  4. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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?

  1. 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.

  2. 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.

  3. 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.

  4. 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?

  1. 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.

  2. 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
  3. 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.

  4. 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.

  5. 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?

  1. 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.

  2. 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
  3. 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.

  4. 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.

  5. 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?

  1. 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.

  2. 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%.

  3. 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
  4. 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.

  5. Open weights, and what is open

    All three stages are public at huggingface.co/Prahaladha/flappy-world-model. The bird in long real-key runs is still lost too often.

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.