InsightsInside the program
Inside the program
This program is billions of characters long, and every one of them is one of the eight commands of brainfuck. Together they run a chat model. Nearly all of those characters do one small thing: they put a number into memory. A short stretch at the end uses those numbers to turn your question into an answer.
Two very unequal parts
The program is one line of text, and it runs from its first character to its last [1]. The strip below is that line drawn to scale. It has two parts, and the second is so short that it is the sliver at the right end. Under the strip, the eight commands are counted. Press the buttons to count one part at a time:
Counted in the program itself, character by character.
Watch the bar for “step right” when you switch. In the long part it is all but gone, and the plus and minus signs make up more than half of the text. In the short part, left and right are nearly in balance and the plus and minus signs are few. So the two parts are not only unequal in length. They are different kinds of text.
The long part is the writing part: characters, the first of the program. It moves through memory in one direction. It steps one place to the left, adds or takes off a little, and steps on, from its beginning to its end. It reads nothing and prints nothing. When it is done, the numbers of the model are in memory.
The last characters, of the program, are the engine. It reads your question, calculates with the numbers the writing part left behind, and prints the answer. It writes little and walks a lot, back and forth between those numbers. It can be short because it works in loops. The commands [ and ] repeat whatever stands between them, so a calculation that is written once runs for thousands of numbers, and again for every piece of the answer. How long a part is says nothing about how long it runs.
Look at the last two bars with the whole program chosen. The read command , and the print command . each occur once. Both stand inside loops, so each is used for every letter that goes in or comes out. The , is character , about characters into the engine, and everything before it runs the same way whatever you ask. That is why the chatbot can keep a copy of the memory and skip the writing part the next time.That is why the chatbot runs the writing part only once: for the next question it puts back the 1.6 million places in memory that an answer changes.Whatever runs this program carries out the whole writing part again for every question: it cannot yet put back the places in memory that an answer changes.Whatever runs this program has to carry out the whole writing part once before it can read a question.
The mix of commands from the first character to the last
The same count, taken along the program instead of over a whole part. The writing part looks the same from start to end; the mix changes only in the engine, at the far right. Drag along the strip, or use the slider, to read one slice.
measured
The order of the writing part
The writing part fills memory from one end to the other, in the order in which the engine wants to find things. It starts with the table the engine uses last, the output table, then writes layer down to layer 1, then the table of word pieces and the vocabulary. The next section shows where each of these lies. On the program map each one is a field of its own colour.
In this program the output table and the table of word pieces hold the same numbers: the model uses one table for both jobs. The program writes it twice, in two orders, because the engine walks over it in two ways. Together the two copies are of the program. At the edges of every layer lie thin strips of constants, a few thousand characters each.
How much is characters?
estimated from a measured count
Printed, the program fills a library. Below, every book is drawn: a paperback of 300 pages with 1,800 characters on a page, coloured by what its characters write into memory. Point at a book, or press it, to see what is in it.
measured
A row of numbers
Start with a very small one. The machine below has a row of boxes that all hold 0, and five of the eight commands as keys. Press , then + then . and watch the first box:
Press , then + then .
The box went to 65, then to 66, and the machine printed B. Brainfuck has no letters, only numbers: 65 is the number computers use for A, and 66 is the one for B. So , read a letter as a number, + added one to it, and . printed the result as a letter again.
Now press > and then +. A different box changes. The filled box with the small mark under it is where the machine is working, and > and < move that place one box at a time. The row of boxes is called the tape, one box is a cell, and the mark is the pointer. That is all the memory brainfuck has. There are no names for things and no way to jump to a cell: to use a number, the pointer has to walk to it.
I'm showing nine cells here, and each holds a number from 0 to 255. The tape of this program has cells, and each holds a number up to about four billion (0.70 GiB of your computer's memory) (2.45 GiB of your computer's memory) (3.98 GiB of your computer's memory, just under the 4 GiB a browser tab can address in the usual way) (6.81 GiB of your computer's memory, more than the 4 GiB a browser tab can address in the usual way). The commands are the same ones you just pressed.
What is in all those cells? The bar below is the real tape after the writing part has run, drawn to scale, with cell 0 at the left. Each colour is one kind of content. Press a colour to read what lies there, or drag along the bar, or use the slider under it, to move a marker over the tape. A press on a layer zooms in to it:
Read from the tape the program writes. Pale stretches are cells with nothing in them. measured
Almost the whole bar is filled, and almost all of the filling is of one kind: weights. A weight is one of the numbers the model learned when it was trained. A model is a very long list of them plus a fixed recipe for multiplying and adding them, so a tape full of weights is the model.
The model does not read letters. It reads word pieces: a word piece is a word or a part of a word, such as “capital” or “ing” (elsewhere it is called a token). The stretch marked W, at the right, is the table of word pieces. It has one row of numbers for each of the word pieces the model knows. The stretch marked O, at the far left, is the output table, which is used at the very end to choose the next word piece.
Between those two lie stretches. Each is a layer: one round of the model's calculation, with weights of its own. Every word piece goes through all . They are of two kinds and come in groups of four; press Four layers to see one group. In three of every four, the DeltaNet layers, the weights marked D mix in what came before. Such a layer does not look back at the earlier word pieces. It keeps tables of numbers, marked M, writes every word piece into them and reads from them; press One table to see one. The fourth layer of a group looks back, as all layers of brnfck-smollm2-135m, brnfck-qwen2.5-0.5b and brnfck-qwen2.5-1.5b do: the weights marked A are for that, and between them lie 256 memory slots, one for each word piece of the conversation. That is why a conversation here ends after 256 word pieces. The weights marked F, in every layer, are for the part that works on the current word piece alone. All of this memory is empty before you ask anything.
Between those two lie stretches that look alike. Each is a layer: one round of the model's calculation, with weights of its own. Every word piece goes through all . If you pressed One layer you saw that a layer has weights of two kinds. The ones marked A are for attention, the part of a layer that looks back at the earlier word pieces. The ones marked F are for the part that works on the current word piece alone. Between them, marked M, lie 256 memory slots, one for each word piece of the conversation. That is where the model keeps what it has read, and it is why a conversation here ends after 256 word pieces. The first 24 slots are filled before you ask anything: they hold the fixed opening lines that tell the model to be a helpful assistant.
The table of word pieces is on this tape twice: once at the right, and once more at the far left as the output table, in parts. Together the two copies take of the tape.
The parts of the tape, in cells
What lies in the stretch the bar shows now. Zoom the bar and this table follows it. The thin strips of constants are too narrow to see in the bar; with one layer in view they are listed here.
Writing a number without typing it
Brainfuck has no command that means “put 513 here”. From zero, 513 takes 513 plus signs. Written that way, the numbers on this tape would take characters, about times the length of the writing part as it is.
The writing part does something shorter. It is cut into blocks, and each block fills a run of neighbouring cells, at most 8,192 of them [4]. The figure below holds one real block. The box shows the commands that run next, and your browser carries them out. Each row of the table is one cell: what it holds now, and what it must hold when the block is done. Press Step, or drag the slider under the table forwards and back, and watch the column “In the cell now”. Keep going until the line under the table says every cell is right:
The commands are the real text of the program; the numbers they must produce were read from the tape. The table shows ten of the block's cells, the ten the commands reach first. The bar under it stands for all of them. measured
The cells did not get their numbers in one go. The first press gave every cell the same small start value. After that, two kinds of press took turns. One kind walked along the block and gave each cell a few plus or minus signs: those are the columns of signs in the table. The other kind was a single short loop that multiplied every cell by one small number, the ×5, ×7 or ×3 in the headings of the table, and shifted the whole block one cell to the left.
That is how we write numbers ourselves. 513 is a 5, times ten, plus 1, times ten, plus 3: three small digits instead of 513 strokes. The block does the same with every one of its cells at once. A pass of plus and minus signs writes one digit into each cell, and the shared loop does the multiplying for all of them. The loop is written once, however many cells the block has.
The digits here can be negative. Where the loop multiplies by 5 they run from −2 to 2, so such a digit never costs more than two characters. A digit of 0 costs none: the commands just step past that cell.
Two details keep this working. The shared loop stops at the first cell that holds zero, so no cell may be zero on the way: that is what the start value is for. And the digits count from a middle value of the block, not from zero, which keeps them small. The last loop takes the start value off again and puts the middle value back.
brnfck-smollm2-135m, brnfck-qwen2.5-0.5b, brnfck-qwen2.5-1.5b and brnfck-qwen3.5-0.8b were all written by the same encoder, so a block of this program is built like a block of brnfck-smollm2-135m. What differs is what is in the cells: these weights have 8 bits where brnfck-smollm2-135m's have 10, and there are strips of larger constants. Choose one under More controls to see a block with many more digits per cell.
brnfck-smollm2-135m, brnfck-qwen2.5-0.5b, brnfck-qwen2.5-1.5b and brnfck-qwen3.5-0.8b were all written by the same encoder, so a block of this program is built like a block of brnfck-smollm2-135m. Its weights have 10 bits, as brnfck-smollm2-135m's do, and between them lie strips of larger constants. Choose one under More controls to see a block with more digits per cell.
The block above needed only a few signs per cell. Is that luck? The next figure counts every weight on the tape by its value. Choose a part of the model to see its weights alone, and slide the marker along the values to read how many weights have each:
Whichever part you choose, the bars pile up in the middle. Most weights are close to zero. (The tape stores a weight as the weight plus , so that a stored weight is never zero; the chart shows the weights themselves.) Numbers that lie close together are a small distance from the middle value of their block, and a small distance has small digits. Here is every digit the writing part wrote after the first one of a cell, counted:
measured
Most digits are 0, 1 or −1, the three cheapest. That brings the writing part to about characters for each of the cells it fills.
What one cell costs, and how close that is to the shortest possible
Characters per cell
Three ways to write the same tape
How far is this from the best possible? The numbers on the tape carry bits of information per cell. A brainfuck character can carry at most 3 bits, because there are 8 commands. So no way of writing this tape can be much shorter than characters. The writing part uses : times that floor. The floor is estimated: the information on the tape, counted per million cells, divided by 3.
In Qwen2.5 every row of a table of weights has its own scale. Before the weights were stored, each row was multiplied by a power of two of its own, as large as it could be while the largest weight of the row still fits in 8 bits. That is why these values spread wider than those of brnfck-smollm2-135m, and why a cell costs a little more.
In Qwen3.5-0.8B every row of a table of weights has its own scale. Before the weights were stored, each row was multiplied by a power of two of its own, as large as it could be while the largest weight of the row still fits in 10 bits. The values spread wider than those of brnfck-smollm2-135m, which also has 10 bits per weight: a typical weight lies about twice as far from zero (a standard deviation of 55.9 against 27.3), only 16% of them lie between −8 and 8 against 39%, and a cell costs 7.39 characters against 6.14.
The engine
The bar below is the engine alone, characters to , drawn to scale [5]. Each coloured stretch is one section of it, and the list under the bar names them in the order in which they run. Press a stretch of the bar or a row of the list to read what that section does, or drag along the bar:
Read its commands
The real commands of the program, one loop level per indent; a run of the same command is written as a count. The notes on the right are worked out from the shape of each loop.
Read from top to bottom, the list is a recipe. Read the question and cut it into word pieces. Then, for one word piece at a time: fetch its row of numbers from the table, take those numbers through the layers, give every word piece the model knows a score, and keep the one with the highest. Print that word piece. It then becomes the next one to go through, and so the answer grows a word piece at a time, until the model picks a word piece that means “end” or the 256 slots are full.
Look at the four headings that begin “The first layer of a group of 4” to “The fourth”. The sections under them stand in the program once: three DeltaNet layers and one attention layer. One loop runs the four of them times, and each time the pointer has moved on to the next four layers' weights. This works because the engine never names a place. Its commands only say “so many cells to the left”, and every group of four is laid out like the others, so the same commands fit every one of them.
Look at the heading “In each of the layers”. The sections under it stand in the program once. One loop runs them times, and each time the pointer has moved on to the next layer's weights. This works because the engine never names a place. Its commands only say “so many cells to the left”, and the layers are laid out alike, so the same commands fit every one of them.
If you opened Read its commands you saw mostly long runs of < and >. Most of the engine is walking; its longest single walk is cells. The arithmetic is done by a few small patterns that return everywhere. The one to remember is the bit peel, which reads a number one binary digit at a time. Dividing, rounding, comparing and multiplying two numbers are all built on it.
The five patterns the engine is built from
- Move and copy
[->+<]empties one cell into another. A cell can only be read by emptying it, so a copy empties it into two cells and moves one back.- Multiply by a fixed number
[->+++++<]adds five times the cell to its neighbour.- Bit peel
- Doubling a cell pushes its highest binary digit out. By doubling the right number of times, “is this cell zero?” becomes “is this digit a one?”. That reads a number digit by digit, in about 4 steps each.
- Table lookup
- A tree of such tests, with a different fixed number added at the end of every branch. This is how the engine takes an exponential or one divided by a number: it looks the answer up.
- The walk over slots
- A loop whose commands end a fixed number of cells further than they began. The same commands then run on the next slot. This is how the engine visits every earlier word piece.
How this engine differs from those of brnfck-smollm2-135m and the Qwen2.5 programs
Qwen3.5-0.8B is of a newer make than Qwen2.5, and its engine was written for it [5]. It uses the building blocks of the Qwen2.5 engine for the feed-forward part, the scaling, the output table and the walk home. The rest is new:
- Two kinds of layer. DeltaNet layers and attention layers, in groups of four: three of one, one of the other. The text of a group is written once and runs times.
- A memory that does not grow. A DeltaNet layer has heads, and each keeps one table of numbers. For every word piece the engine makes three passes over the columns of a table: what it already says for the key, the difference written in, and what it says for the query. There are no memory slots in these layers.
- A table that fades without being touched. A DeltaNet head forgets a little of its past with every word piece. Instead of multiplying the whole table, the engine multiplies one scale number of the head, and halves the table only when that number gets small.
- The three word pieces before. A short convolution adds the inputs of the last three word pieces to those of this one. They stay on the tape between word pieces.
- Gates. In both kinds of layer the output is multiplied by a gate the layer makes itself, bent by a curve.
- No bias, gains per head. The attention layers have no bias. Every head of the questions and keys is brought to a common size with gains of its own, and only the first of its numbers are turned by position.
- A scale for every row, as in the Qwen2.5 programs, with 10 bits per weight instead of their 8.
- Nothing worked out beforehand. brnfck-smollm2-135m, brnfck-qwen2.5-0.5b and brnfck-qwen2.5-1.5b keep the opening lines of every conversation in memory. This one starts from an empty memory and runs every word piece, from the first.
- One table, used twice, as in Qwen2.5: word pieces, and the output table in parts of rows.
How this engine differs from brnfck-smollm2-135m's
The two Qwen2.5 models are of another make, and their engine was written again for them [5]. It has the same 23 sections as brnfck-smollm2-135m's, but most of them work differently:
- A scale for every row. Weights are 8 bits here. Each row of a table of weights has its own number of doublings, kept in a strip next to the place where the totals are rounded.
- A bias. The totals of the first table of a layer get a constant added, one for each of its rows.
- Gains kept apart. Every scaling multiplies each of the running numbers by a gain of its own, from a strip of constants, and rounds again.
- The running numbers travel as a strip. With a scale for every row they cannot be put into the totals beforehand. They ride along and are added when the totals are rounded.
- Shared keys. The model looks back with heads but stores only keys and values per word piece: heads share one.
- One table, used twice. There is no separate output table. The scores come from the table of word pieces itself, which lies on the tape a second time, cut into parts of rows.
- A bigger vocabulary, stored smaller. word pieces. The text of a word piece is not stored: three cells say which two smaller word pieces it is made of, and printing follows those rules down to single bytes.
Since the engine can only walk, one word piece is one long trip. The next figure is a recording of such a trip. Across is the tape, as in the bar of the previous section. Down is time. The dot is the pointer. Press Play, drag the slider (or the picture), or jump from section to section:
A recording of one word piece of one question. The line has the colour of the section that is running. measured
The dot began at the right end of the tape, where the number of the word piece is kept. It ran through the table of word pieces to fetch one row. Then it worked its way to the left, layer after layer. At the far left it went through the output table and found the best next word piece. And then it walked all the way home.
The weights do not stand still either. To multiply, the engine moves a column of weights across a small gap of free cells and adds it to running totals on the way. After a layer, its weights lie one gap further along. The walk home puts every column back where it was, so that the next word piece finds the same tape.
Where the time goes
An interpreter is a program that carries out brainfuck commands, and a step is one thing this one does. A step is not always one command: a run of plus signs is one addition, and a short loop that only moves or multiplies a number is done at once (exactly what counts). So what follows are counts of steps. They are not seconds, and they are not single commands.
Each bar below is one section of the engine with the steps it took for one word piece. The strip above the bars adds them up by part of the model. Press a bar to see that section layer by layer, and slide along the layers to read each one:
- FFeed-forward
- AAttention
- DDeltaNet
- OOutput table
- WTable of word pieces
- MMemory of earlier pieces
- ·Walking
In every layer
measured
The bars are very unequal. A few carry nearly the whole word piece, and they share a name: “Weights × numbers”. Those are the sections in which every weight of a table is multiplied by a number that is passing through. Press one of them and the chart underneath is almost flat: every layer costs about the same.
That was one word piece. The next chart has one bar for every word piece of a recorded run, coloured by part of the model as before. Drag the slider along them, or drag across the bars. The figure above follows it:
measured
These counts come from two questions. The trip of the pointer and the chart by layer show one word piece of the first one.
Steps differ in what they cost a computer, so a share of the steps is not the same as a share of the seconds. The seconds that were measured are in the table under How it was checked.
What kind of step, over the whole run
SmolLM2, Qwen2.5 and Qwen3.5 side by side
brnfck-smollm2-135m, brnfck-qwen2.5-0.5b, brnfck-qwen2.5-1.5b and brnfck-qwen3.5-0.8b were made in the same way, and their models differ by a factor of eleven in size. This comparison is the same whichever program is chosen at the top. brnfck-smollm2-135m was counted to its last position, brnfck-qwen2.5-0.5b, brnfck-qwen2.5-1.5b and brnfck-qwen3.5-0.8b for one short question each; the table says for every number where it comes from.
The program: writing part, and the engine as the dark end
The tape: what the cells hold
The share of each command in the whole program
The tick marks the program chosen at the top. measured
Steps for one word piece at position 24, by part of the model
How much larger than in brnfck-smollm2-135m (each doubling is the same length)
In brnfck-smollm2-135m, brnfck-qwen2.5-0.5b and brnfck-qwen2.5-1.5b position 24 is the first word piece after the fixed opening lines; brnfck-qwen3.5-0.8b keeps none in memory and runs every position from 0, so its position 24 is the 25th word piece of the run. Position 24 was counted in all four. measured
How it was checked
Three things were checked separately [2]. Is the program what it claims to be: nothing but the eight commands, brackets that match, one read command? Does the writing part leave exactly the right numbers on the tape? And does the whole program print the right text?
The second check is one you can look at. The tape that this program writes was compared with a tape made in another way, by an earlier version that read its weights from a data file. Both tapes were cut into pieces of 1,048,576 cells, and each piece was given a checksum: a short fingerprint that comes out different if a single cell differs. Each square below is one piece of the tape. Drag the slider to walk along it:
A marked square means both tapes gave the same SHA-256 checksum there. from the project's log
Every square is marked. All checksums are equal, over all cells: the writing part writes the tape it should.
For the third check the whole program was given questions, and what it printed was compared, letter for letter, with a plain version of the same model that calculates in whole numbers. Each row of this table is one of those runs:
What these checks do not show
The right text is not a right answer. The program follows a version of the model in which every number was rounded to a whole number, because brainfuck has no others. That version picks the same next word piece as the original model in 94.8% of the tested cases (345 of 364).That version does not always pick the same next word piece as the original model. And the original is a small model that is often wrong.
Only the questions in the table were compared. And the whole program has never been run one command at a time: it was run by interpreters that do simple loops at once. Running one command at a time, with a check on every move of the pointer, was done for small test models only.
Every limit of these measurements
- Only ten questions were compared. The engine has no known difference with the reference, on these ten and on 279 generated word pieces over 5 prompts. Other questions were not compared.
- Only one question was compared. The program printed the reference text for it, and the check stopped the run after 30 word pieces. Other questions were not compared.
- Only a few questions were compared. The program printed the reference text for questions. Some of those runs were stopped by the check after 30 word pieces. Other questions were not compared.
- Never run one command at a time. For this program it would take far too long.
- Steps were counted, not seconds. The seconds in the tables come from the faster interpreter that the project's checks use.
- Measured from the command line. The tape of this program is 6.81 GiB, more than a 32-bit memory can hold. Everything here about it was measured with command-line tools on 64-bit memory. Your browser only reads the results.
- No meaning of single weights. It is known which table a block belongs to, not what its numbers stand for.
- Reading the commands needs a server that can send part of a file. The block and the pieces of the engine are read out of the program itself. Where a server cannot send a part of a file, only the pieces stored beside the figures are shown.
- Measured unevenly. Everything about brnfck-smollm2-135m, brnfck-qwen2.5-0.5b, brnfck-qwen2.5-1.5b and brnfck-qwen3.5-0.8b and their tapes was measured in the same way for all four. The step counts were not: brnfck-smollm2-135m was run to its last position, brnfck-qwen2.5-0.5b, brnfck-qwen2.5-1.5b and brnfck-qwen3.5-0.8b for one short question each.
- One computer, one browser. The figures and the chatbot were tried in one desktop Chromium browser.
Every check of the program, the tape and the answers
From the log of the project's own checks. from the project's log
How the figures above were checked
Where to go next
The program map shows the same parts as a picture, down to single commands. The lessons start from programs of a few characters and build up to the patterns used here.
- <>Program mapThis programSee every character of it in one picture, and zoom in to the block or the section you read about here.
- +LearnTwo coursesStart from the eight commands, then build the copy, the multiplication and the bit peel yourself.
- -How it worksArticleFollow one question from your keyboard to the first printed word piece.
- ,Ask brnfck-smollm2-135mChatbotRun this program on a question of your own.
- ,Ask brnfck-qwen2.5-0.5bChatbotRun this program on a question of your own.
- ,Ask brnfck-qwen2.5-1.5bChatbotRun this program on a question of your own.
- ,Ask brnfck-qwen3.5-0.8bChatbotRun this program on a question of your own.
- []Compare the modelsArticleWhat Qwen3.5-0.8B keeps instead of looking back, next to the models that look back in every layer.
Details and sources
What counts as a step
One operation of the project's compile step (compiler/core/bf.js). It turns a run of + and − into one addition, joins a walk to the command that follows it, and does a loop that only adds and moves as one multiplication. Each of those leaves the tape as running the commands one at a time would. The chatbot uses a faster interpreter, which turns the same operations into machine code.
The program file
The whole program is one file. This is the one shown now.
Sources
- The program: () and its description
, which holds the sizes, the SHA-256 and the checksums of the tape. - The project's checks:
compiler/smollm/pure/verify.jsand its logcompiler/smollm/pure/out/verify.log.compiler/qwen/program/verify_pure.jsand its log.compiler/qwen3.5/program/verify_pure.jsand its log. - What wrote the writing part:
compiler/smollm/pure/encode.js(the encoder of brnfck-smollm2-135m, brnfck-qwen2.5-0.5b, brnfck-qwen2.5-1.5b and brnfck-qwen3.5-0.8b) andcompiler/smollm/pure/build.js.compiler/qwen/program/build_pure.js.compiler/qwen3.5/program/build_pure.js. - The generators of the engine:
compiler/smollm/engine/gen.js,compiler/smollm/engine/stations.js,compiler/smollm/tokenizer/gen.js,compiler/smollm/program/gen.jscompiler/qwen/engine/gen.js,compiler/qwen/engine/stations.js,compiler/qwen/tokenizer/gen.js,compiler/qwen/program/gen.js, with the arithmetic they reproduce incompiler/qwen/engine/spec.js,compiler/qwen3.5/engine/gen.jswith its two layer kindscompiler/qwen3.5/engine/mixers/deltanet.jsandcompiler/qwen3.5/engine/mixers/attention.js,compiler/qwen3.5/tokenizer/gen.js,compiler/qwen3.5/program/gen.js, the parts of the Qwen2.5 engine they use, and the arithmetic they reproduce incompiler/qwen3.5/engine/spec.js, and the building blocks inpatterns.js. - The model: SmolLM2-135M-Instruct by Hugging Face, published under the Apache 2.0 licence. 30 layers, 576 numbers per word piece, 49,152 word pieces.The model: Qwen2.5-0.5B-Instruct by Alibaba Cloud, published under the Apache 2.0 licence. 24 layers, 896 numbers per word piece, a vocabulary table of 151,936 rows.The model: Qwen2.5-1.5B-Instruct by Alibaba Cloud, published under the Apache 2.0 licence. 28 layers, 1,536 numbers per word piece, a vocabulary table of 151,936 rows.The model: Qwen3.5-0.8B by the Qwen team of Alibaba Cloud, published under the Apache 2.0 licence. 24 layers, 18 of them Gated DeltaNet, 1,024 numbers per word piece, a vocabulary table of 248,320 rows. Its new kind of layer is explained on Compare the models.
- The folded line of the program map: D. Hilbert, 1891, on a continuous line that passes through every point of a square.
- The colours of the figures follow the palette of M. Okabe and K. Ito (2008), made to stay apart for people with colour-blindness.
- Brainfuck: a language designed by Urban Müller in 1993.
How the numbers behind the figures were made
The scripts in tools/map-data/ read the real programs and the project's logs and write the data the figures load, one folder for each program. None of them changes a program. The eight below make the data of this page; beside them are their helpers (models.js, interp.js, instrument.js, regions.js), lens.js for what every layer would say on How it works, and train.js for the four programs that train a network.
| Script | What it does | Used for |
|---|---|---|
writer.js | Runs the writing part to get the tape. Runs the project's encoder again on that tape while noting every block, and checks that its output, with the engine behind it, has the SHA-256 of the program. Counts commands, weight values, digits, and the information on the tape. | The two parts, the tape, the block, the digits |
sections.js | Runs the engine's generator again while noting where every named section lands. Compares the result with the end of the program, character for character. | The sections of the engine, the program map |
tiles.js | Reads the program once and makes the pictures of the program map and the pieces of text behind it. | The program map |
depth.js | Reads the program once and notes how deep its loops are nested along it. | The program map, the layout "Skyline" |
stops.js | Reads the engine and finds its only comma and its only full stop: the section of each, how many loops are open there, the characters around it, and the mix of commands along the way. | The figure "Once, in more than a billion characters" on How it works |
trace.js | Runs the real program on a question with an interpreter that counts every step by section, layer and word piece and notes where the pointer is. The printed answer must equal the reference. It can stop after a number of positions. | The trip of the pointer, the step counts |
checks.js | Reads the logs of the project's own checks. It runs nothing. | The checksums, the checks, the questions |
compare.js | Gathers the main numbers of brnfck-smollm2-135m, brnfck-qwen2.5-0.5b, brnfck-qwen2.5-1.5b and brnfck-qwen3.5-0.8b from what the other scripts wrote. | SmolLM2, Qwen2.5 and Qwen3.5 side by side |
The words used here, in one line each
- Command
- One of the eight characters brainfuck understands. Each does one small thing.
- Tape
- The program's memory: one long row of cells.
- Cell
- One place on the tape. It holds one whole number.
- Pointer
- The cell the program is at. The commands
<and>move it one cell. - Weight
- One of the numbers the model learned in training.
- Word piece
- A word or a part of a word. The model reads and writes text one word piece at a time. Elsewhere it is called a token.
- Layer
- One round of the model's calculation, with weights of its own. has of them.
- Writing part
- The long first part of the program. It puts the model on the tape and neither reads nor prints.
- Engine
- The short last part. It reads the question, calculates with the weights and prints the answer.
- Interpreter
- The software that carries out a brainfuck program.
- Step
- One operation of the interpreter. See what counts as a step.