Tape and Stack Languages

Brainfuck Variants and Dialects

A survey of the kinds of changes found in languages derived from Brainfuck, from cell size and end-of-input behavior to bit cells and extra commands.

On this page
  1. Why there are so many dialects
  2. Differences in the implementation
  3. Deliberate changes to the language
  4. Is it still Brainfuck?
  5. Compatibility checks that help
  6. What the variants teach

Why there are so many dialects

Brainfuck is described in a few lines, and the description leaves several details open. How big is a cell? What happens when you decrement zero? What does the input command do at the end of the input? Because the original rules are so short, every interpreter writer fills in the gaps, and those choices have become informal dialects. On top of that, many people have used Brainfuck as a base and changed it on purpose to see what happens.

It helps to separate the two cases. Some differences are accidents of implementation that a careful program can avoid. Others are deliberate changes that create a different language.

Differences in the implementation

These are the changes that matter if you want one program to run everywhere.

Cell size. Cells are very often 8-bit, holding values from 0 to 255, but 16-bit and 32-bit cells exist, and some interpreters use arbitrarily large integers. A program that relies on a cell wrapping from 255 to 0 will misbehave on a bigger cell.

Overflow and underflow. Wrapping around is common, but some interpreters stop with an error, and others let the number grow. This is a close cousin of the arithmetic edge cases that every interpreter author meets.

Tape length and bounds. The classic tape is often described as 30,000 cells, though that number is a convention rather than a rule. Moving left of the first cell may be an error, may wrap to the far end, or may reach into more tape that grows on demand.

End of input. When the program reads and there is nothing left, the cell may be set to 0, set to -1 (or 255), or left unchanged. Programs that read until the end of input are the ones that notice. How an interpreter treats these details is part of interpreter input output design generally.

Newlines. Some systems translate line endings, so a program that prints a newline may produce different bytes on different platforms.

A portable program usually avoids relying on any of these. It stays inside a small tape, keeps values within 0 to 127 where possible, and treats end of input as a clear signal that does not depend on a specific value.

Deliberate changes to the language

Beyond the implementation gaps, people have changed the language itself. The changes tend to fall into a few families.

Smaller or different cells. Some derivatives use a single bit per cell, which forces a very different style of programming. Smallfuck is an example in this spirit, with a finite tape of bits. Boolfuck, similarly, works with bits and changes how input and output are packed.

Added commands. The most obvious extension is a set of new symbols: commands for debugging, for resetting a cell, for printing numbers, or for jumping several cells at once. The motivation is almost always to shorten programs or make them easier to inspect. One known example, pbrain, adds procedures, so that a block of code can be named and called.

Different shapes of memory. A few dialects keep the instructions but replace the tape with a different structure, such as two-dimensional storage or a stack. These start to shade into entirely different languages.

Removed commands. The reverse also occurs. Dropping the input command, for instance, gives a language that can only produce output. Such restricted forms are popular for theoretical write-ups because proofs become shorter.

Different surface syntax. Some derivatives keep the semantics exactly and only rename the symbols. Those are better described as reskins, which is a separate topic.

Is it still Brainfuck?

There is no authority that decides, and the esolang culture around these languages is relaxed about it. A practical test: if a typical Brainfuck program needs changes to run, it is a different language. If it runs unchanged and behaves the same on ordinary inputs, it is probably the same language with different implementation choices.

Compatibility checks that help

If you write a program and want it to be portable, a short list works well:

  • Do not depend on wrap-around.
  • Do not assume a tape longer than a few thousand cells.
  • Do not move left of the starting cell.
  • Do not rely on a particular value at end of input.
  • Test it on at least two interpreters.

What the variants teach

Dialects show the difference between a language and its implementations in a very direct way. They also illustrate good language design: tiny rule sets leave openings, and the openings become the variants. A very small instruction set is attractive, as discussed with minimalist languages, but the price is that every unspecified detail turns into a fork. Even so, none of these changes alters the basic fact that matters for Turing completeness: the language still needs a loop and an unbounded store, and as long as those survive, it remains capable of the same computations in principle.

Finally, program legibility often gets worse in the extended variants, not better. Extra symbols can shorten source but they add to the code readability problems that all of these languages already have.

The complete guide to tape and stack languages

Tape and Stack Based Esoteric Languages
Read the guide

Keep exploring

Guides from other topics

All topics →