LearnReferenceImplementations

Implementations

The simplest brainfuck interpreter is a loop of a dozen lines, and the language's author says you can write one in half an hour [1]. A fast one is a different thing. It reads the program first, merges runs of the same command, recognises loops that clear, copy or multiply, and may then translate the result to machine code. Each of these steps has been measured: together they made six test programs between 2.4 and 130 times faster in one study [2].

One command at a time

An interpreter needs a tape, a pointer, a position in the program, and one action for each of the eight commands. Written in JavaScript:

function run(program, input) {
  const tape = new Uint8Array(30000);
  let p = 0, out = '';
  for (let i = 0; i < program.length; i++) {
    const c = program[i];
    if (c === '+') tape[p]++;
    else if (c === '-') tape[p]--;
    else if (c === '>') p++;
    else if (c === '<') p--;
    else if (c === '.') out += String.fromCharCode(tape[p]);
    else if (c === ',') tape[p] = input.length ? input.shift() : 0;
    else if (c === '[' && tape[p] === 0) i = match(program, i);
    else if (c === ']' && tape[p] !== 0) i = match(program, i);
  }
  return out;
}

The helper match walks through the program to find the partner of a bracket, counting brackets it passes on the way. This interpreter is correct. (It makes the usual choices: 8-bit cells that wrap, 0 at the end of the input, no check at the ends of the tape. See Where implementations differ.)

Müller's own interpreter of 1993 is of this kind, about a thousand characters of C. It handles a loop by calling itself for the body [3].

It is also slow, for two reasons. It searches for the matching bracket again every time a loop goes round. And it does each + of a row of fifty as a separate piece of work. The steps below remove such waste, one kind at a time.

Step 1: find the brackets once

Before running, go through the program once and write down for every bracket where its partner is. During the run a jump is then a single look-up. Eli Bendersky measured this on the Mandelbrot program: 38.6 seconds became 18.4 [4].

Step 2: merge runs

Brainfuck programs are full of rows of the same command. +++++ can be one operation, "add 5"; <<< one "move 3 left". A + next to a - cancels out. So the interpreter first translates the program into a list of operations with a count, and runs that list. This is called run-length merging or contraction.

Mats Linander counted how much of real programs can be merged this way: about three quarters of the commands in the Mandelbrot program and two others, 40 to 60 per cent in three more [2]. In Bendersky's series this step took the Mandelbrot run from 18.4 to 11.9 seconds [4].

Step 3: recognise simple loops

The big gains come from loops whose whole effect can be worked out without running them.

Clear loops
[-] counts a cell down to 0. That is "set the cell to 0": one operation in place of up to 255 rounds, or up to four billion with 32-bit cells. On cells that wrap, [+] does the same [2].
Copy and multiply loops
[->+++>+<<] takes one off the starting cell each round, adds 3 to the next cell and 1 to the one after, and comes back. After the loop the starting cell is 0 and the others have grown by 3 times and 1 times its old value. That is two multiplications and a clear, at a fixed cost. The loop qualifies when its body has only + - < >, ends on the cell where it began, and changes that cell by exactly one per round [2] [5].
Scan loops
[>] and [<] walk to the next cell that holds 0. A processor can search memory for a zero much faster than one cell per step. Linander found this made the self-interpreter dbfi more than three times faster and did little for the other programs [2].

The program below has a loop inside a loop, and adds 4 to cell 2 each time the inner loop goes round. The sliders set how often each loop goes round. The two counts under the cells are the number of steps of the same run, counted two ways. Press Play, and then put a print inside the inner loop:

Both counts describe the same run with the same result: with the sliders as they were, 64 in cell 2. The merged count replaces the inner loop by one multiplication, so the inner loop is one step and the outer loop, which holds another loop, still goes round four times. When you put a print command inside the inner loop, it no longer has the simple shape: a loop that prints must really go round, because each round has an effect outside the tape. The merged count went up accordingly. The counts are those of the interpreter that Brainfuck basics and Build toward a chatbot use.

In Bendersky's series, recognising such loops was the largest single step: the Mandelbrot run went from 11.9 to 3.9 seconds [4].

Step 4: fold the moves away

In >+>++<< the pointer goes out and comes back. Nothing needs to move: the piece means "add 1 at one cell to the right, add 2 at two cells to the right". An implementation can give every operation an offset from the current position and move the pointer only once, at the end of a stretch without loops. Linander calls this "operation offsets" and found the gain real but modest [2]; bfc calls it "reorder with offsets" [5].

The program below has twelve commands. Show its run one command at a time, and then with runs and simple loops merged, and step through both:

One command at a time the program takes 24 steps; merged, 3. The program prints a character with code 6 either way. Look at the middle step: a whole loop became one line of arithmetic, and the moves are folded into the steps around it.

The table below puts the optimisations of steps 2, 3 and 4 side by side.

The standard optimisations. Names differ between authors; the first column gives the common ones [2] [5].
NameBrainfuckBecomes
Run-length merging, contraction+++++   <<<add 5   move 3 left
Clear loop[-]set the cell to 0
Copy loop[->+>+<<]add the cell to two others; set it to 0
Multiply loop[->+++<]add 3 times the cell to the next; set it to 0
Scan loop[>]search right for a cell that holds 0
Offsets>+>++<<add 1 at offset 1; add 2 at offset 2; do not move

All of them keep the result exactly the same. Applied together to six real programs, they gave speed-ups from 2.4 times (the compiler awib) to about 130 times (a Towers of Hanoi program) in Linander's measurements [2].

Compiling

An interpreter looks at each operation, decides what it is, and does it. A compiler translates the program once into something a machine runs directly, so the deciding is done before the run.

To C
The easy way: each command becomes a line of C, exactly as in the table of the 1993 README (the last column of table of the eight commands on the page The language) [3], and a C compiler does the rest. Linander's measurements were made this way [2], and awib and esotope-bfc write C [6] [7].
To machine code
The original implementation was a compiler of this kind. For each command it copies a few bytes of ready-made machine code to the output. To reach 240 bytes Müller let some bytes of the compiler do three jobs at once: as instructions the compiler itself runs, as the code it copies out, and as part of a table [1] [3]. Brian Raiter's compiler for Linux works in the same spirit in 166 bytes [8] [9].
While running (JIT)
A just-in-time compiler makes the machine code in memory and jumps into it. In Bendersky's series a simple one ran the Mandelbrot program in 2.89 seconds and one with the optimisations described here in 0.93 seconds, against 38.6 for the first interpreter [10] [4].
To WebAssembly
WebAssembly is the machine code of web browsers. Small compilers from brainfuck to it exist, with and without optimisation [11] [12]. The chat pages of this site work this way: they translate a program to WebAssembly, merging runs and doing simple loops in one operation, and that is what makes programs of billions of commands usable [13].
One program, six implementations: the run time of the Mandelbrot viewer in Eli Bendersky's series, on his computer in 2017 [4] [10]. Read down the last column.
ImplementationWhat it addsSeconds
Simple interpreternothing38.6
Interpreter, take 1brackets found once18.4
Interpreter, take 2runs merged11.9
Interpreter, take 3simple loops recognised3.9
Simple JITmachine code, no optimisation2.89
Optimising JITmachine code, plus what takes 1 to 3 add0.93

The same six run times as bars, to scale from zero:

Run times of the Mandelbrot viewer in Eli Bendersky's series, on his computer in 2017. measured

Notice how the bars shrink from the first to the last: the run time falls from 38.6 seconds to 0.93.

How far it can go

A compiler knows the tape starts empty. If a program reads no input, the compiler can simply run it while compiling and keep only what it prints. Kang Seonghoon's Esotope compiler was made to do this: its README shows a "Hello World!" program that compiles to a single statement that prints the text, and says it was the first compiler to manage that [7]. Wilfred Hughes's bfc does the same under the name "speculative execution", next to dead code removal and an analysis of how many cells a program can reach [5].

For programs that read input, part of the work can still be done ahead. The chat programs of this site use the idea by hand: the first billion commands of brnfck-smollm2-135m read no input, so the tape they leave is the same for every question, and the page keeps a copy of it [13].

The smallest

Brainfuck was made to have a small compiler, and making it smaller became a sport.

Amiga compiler, Urban Müller, 1993 [3]
240 bytes
Linux compiler, Brian Raiter, 1999 [8] [9]
166 bytes
DOS interpreter, INT-E, Hugi competition 6, 1999 [14] [15]
98 bytes

Smaller ones are reported. Müller mentions an interpreter of 57 bytes for Intel processors that he could not find again in time for his talk [1], and the Esolang wiki lists others [15]. We did not check any of those.

Well-known implementations

A selection of implementations, each with what it is known for. The descriptions follow the authors' own pages.
NameByWhat it is
bfc and bfiUrban Müller, 1993The original compiler for the Amiga and the portable interpreter in C that came with it [3]
bf.asmBrian Raiter, 1999A compiler in 166 bytes for Linux, with commented source [8] [9]
qdb, sbi, bcci, dbcDaniel CristofaniSmall interpreters and a compiler in C: one quick, one "clean and simple, but slow", one strict for contests [16]
TritiumrdebathInterpreter, compiler and JIT in one. Its README claims it is "simply the fastest BF interpreter you'll find"; supports any cell size. The repository also holds translators from brainfuck to many languages [17]
bfcWilfred Hughes"An industrial grade compiler", with documented optimisations and helpful error messages [5]. (Not Müller's bfc: only the name is the same.)
Esotope Brainfuck CompilerKang SeonghoonTranslates brainfuck to short, readable C; runs as much as it can at compile time [7]
awibMats LinanderAn optimising compiler written in brainfuck itself [6]
bfmrjameshamiltonOne optimising compiler with many outputs: JVM, C, LLVM IR, ARM assembly, WebAssembly, JavaScript [12]
bfwasmSurmaA small compiler to WebAssembly that does not optimise, by its own description [11]
This site's playgroundthis siteRuns in the browser, with stepping and a view of the tape

There are far more. The Esolang wiki keeps a long list that also has implementations in hardware: processors and small computers whose machine language is brainfuck [15]. And a brainfuck interpreter is one of the test programs in a well-known comparison of programming languages, written once in each of them [18].

Testing an implementation

If you write one, test it on more than "Hello World".

Limits

Read next

Notes and sources

Where a source is a community wiki or the author's own account, the entry or the text says so.

  1. Urban Müller, talk at Tamedia TX, 2017, read as the community transcript on the Esolang wiki: Tamedia TX 2017 transcript. The transcript says its last page is missing. Wikipedia gives the title as "Brainfuck, or how I learned to change the problem" and the date as 13 June 2017; we did not watch the recording. Opened 11 October 2026.
  2. Mats Linander, "brainfuck optimization strategies", 7 January 2015: the article on calmerthanyouare.org. Opened 11 October 2026.
  3. Urban Müller, brainfuck-2.lha, Aminet, dev/lang, dated 9 June 1993: the package page on Aminet. We read the files in the archive: README, bfc.asm (the compiler), bfi.c (the interpreter) and the six example programs. Sizes and dates are those of the archive listing. Opened 11 October 2026.
  4. Eli Bendersky, "Adventures in JIT compilation: Part 1, an interpreter", 20 March 2017: part 1 on eli.thegreenplace.net. Opened 11 October 2026.
  5. Wilfred Hughes, bfc: the bfc site, its page "Optimisations" and the repository. Opened 11 October 2026.
  6. Mats Linander, awib, README dated 14 September 2014: matslina/awib on GitHub. Opened 11 October 2026.
  7. Kang Seonghoon, Esotope Brainfuck Compiler, README: lifthrasiir/esotope-bfc on GitHub. Opened 11 October 2026.
  8. Brian Raiter, "Brainfuck: An Eight-Instruction Turing-Complete Programming Language": the brainfuck page on muppetlabs.com. Opened 11 October 2026.
  9. Brian Raiter, bf.asm, copyright 1999: the assembly source on muppetlabs.com. Opened 11 October 2026.
  10. Eli Bendersky, "Adventures in JIT compilation: Part 2, an x64 JIT", 2017: part 2 on eli.thegreenplace.net. Opened 11 October 2026.
  11. Surma, bfwasm: surma/bfwasm on GitHub. Opened 11 October 2026.
  12. mrjameshamilton, bf: mrjameshamilton/bf on GitHub. Opened 11 October 2026.
  13. This site's own project. Counts and measurements are on How it works: the numbers and How do I know it is real?; the earlier work we know of is under Has this been done before?. Opened 11 October 2026.
  14. Hugi Size Coding Competition, list of past competitions: the competition archive on hugi.scene.org. Competition 6 (1999) asked for a brainfuck interpreter. Opened 11 October 2026.
  15. Esolang wiki, "Brainfuck implementations": the wiki's list of implementations. Opened 11 October 2026.
  16. Daniel B Cristofani, brainfuck.org: the main page. The page licenses his programs under Creative Commons Attribution-ShareAlike 4.0. Opened 11 October 2026.
  17. rdebath, Brainfuck repository: rdebath/Brainfuck on GitHub. Holds the Tritium interpreter, translators to many languages, test programs and a copy of Müller's 1993 compiler. Opened 11 October 2026.
  18. kostya, benchmarks: kostya/benchmarks on GitHub. Opened 11 October 2026.
  19. Daniel B Cristofani, tests.b: test programs for implementations on brainfuck.org. Opened 11 October 2026.
  20. Daniel B Cristofani, "The Epistle to the Implementors": the epistle on brainfuck.org. Opened 11 October 2026.