GPT-2, running in your browser
This is GPT-2: the same tokenizer algorithm, the same block, the same wiring, scaled down from 124 million parameters to 940,800 so it fits in a page, and trained on children's stories so that something that small can still write. Type anything. Every number below is computed in your browser by the model, and the machine moves because of those numbers.
- 940,800 parameters
- 4 blocks
- 4 heads
- 128 wide
- 1,024 tokens
- 128 context
- 80 million training tokens
Text becomes a row of integers
A regex first splits the text into words, numbers and punctuation, each carrying its leading space. Each piece is spelled out as raw bytes, then merged pair by pair in the order the merges were learned: the earliest-learned pair that is present always merges first. What is left is a list of vocabulary entries, and their row numbers are the tokens.
GPT-2 learned 50,000 merges on 40 GB of web text; this one learned 767 on TinyStories, so common story words are a single token and anything else falls back to pieces. Because it starts from bytes, no input is ever out of vocabulary.
Each token looks up a row, and so does its position
The token id picks one row of a 1,024 × 128 table; the position picks one row of a 128 × 128 table. Their sum is the token's starting state, a vector of 128 numbers that every later stage reads and adds to: the residual stream.
Nothing told the model which words are alike. Training pushed rows of words used in similar places to point in similar directions, which is why the nearest rows below tend to make sense.
Each token gathers from the ones before it
In every block, each position makes a query, a key and a value. A position's query is compared with the keys of every position up to and including itself, the scores are scaled by √dhead and pushed through a softmax, and the weights mix the values. The mask means a token can never see the future, which is what makes the model a predictor.
Each block runs 4 heads in parallel on 32-wide slices, so each can learn a different habit: look at the previous token, look back at a name, look at the start. Pick a block and a head; the threads on the machine follow your choice.
An MLP rewrites each position on its own
After attention, each position passes through a two-layer network on its own: 128 up to 512 numbers, a GELU, and back down to 128. The result is added to the stream, never written over it. Two thirds of every block's parameters live here, and most of what a model knows about words is thought to be stored in these weights.
The dial on each tier of the machine is this stage's write: how far the MLP moves the selected token's vector, as a share of the vector's length.
Scores for every token in the vocabulary
After the last block, a final LayerNorm and one matrix multiply score all 1,024 tokens. GPT-2 reuses the embedding table for this (weight tying): the score of a token is how well the final vector lines up with that token's own row.
Apply the same read-out to the stream after each earlier block and you can watch the guess form. This is the "logit lens": early blocks often guess something generic, and later blocks sharpen it.
Pick one, append it, run again
The scores become probabilities through a softmax. Temperature divides the scores first: below 1 sharpens the distribution, above 1 flattens it, and 0 always takes the top token. Top-k then keeps only the k most likely and renormalises. A seeded random draw picks one token; it is appended to the text, and the whole pass runs again for the next one. That loop is all "generation" is.
The five plungers on the machine are the five most likely next tokens at your settings; the lit one is the draw.
The whole forward pass, in seven lines
For a sequence of token ids t1…tn, with WE the token table and WP the position table:
That is all of it. GPT-2 small, medium, large and XL differ from each other, and from this miniature, only in the four sizes: blocks, width, heads and context. The table recomputes each model's parameter count from those sizes with the same formula, and the first row is checked against the tensors this page actually loaded.
Where the parameters are
Per block: the attention projections hold 4·d² (query, key, value and output) and the MLP holds 8·d² (two d × 4d matrices), plus small biases and LayerNorm gains. So a block is about 12·d² parameters, two thirds of them in the MLP.
At d = 128 the embedding table is a large share of this model (16%); in GPT-2 small it is 31%, and in GPT-2 XL under 6%, because blocks grow with d² and depth while the table grows only with d. A model this small spends a third of its weights just naming tokens.
What this miniature can and cannot do
It has read about 80 million tokens of simple stories, nothing else. Its validation loss is 1.73 nats per token (perplexity 5.6), so it reliably continues a story with names, plain grammar and a small vocabulary, and it has no facts about the world: ask it for a capital city and it will tell you about a park.
Its weights are stored as 8-bit integers with one scale per row, which cost 0.001 nats of loss against the float32 original, measured on the same validation batches. The architecture and every computation are GPT-2's; only the sizes and the data differ.
Trained for this page, in 52 minutes on a laptop CPU
The tokenizer is byte-level BPE trained on the TinyStories validation split with GPT-2's pre-tokenisation pattern and byte table. The model is GPT-2's architecture as written in nanoGPT (pre-LayerNorm, tanh-GELU, learned positions, tied read-out, GPT-2's scaled initialisation of the residual projections), trained with AdamW for 9,754 steps of 64 × 128 tokens on the first 120 MB of the TinyStories GPT-4 training split. The page reimplements the forward pass in plain JavaScript; its logits are checked against PyTorch's for fixed prompts before the page is built.
Sources
- Radford et al., 2019. Language Models are Unsupervised Multitask Learners (GPT-2)
- Vaswani et al., 2017. Attention Is All You Need
- Eldan and Li, 2023. TinyStories: How Small Can Language Models Be and Still Speak Coherent English? (data, CDLA-Sharing-1.0)
- Sennrich, Haddow and Birch, 2016. Neural Machine Translation of Rare Words with Subword Units (BPE)
- Hendrycks and Gimpel, 2016. Gaussian Error Linear Units
- nostalgebraist, 2020. Interpreting GPT: the logit lens
- Karpathy, nanoGPT (the reference implementation the training script follows)
Built with the anatomy kit · every number on this page is computed from the loaded weights