---
title: "A 200-line ray tracer written in Brainfuck, a language with eight commands"
description: "Fixed-point decimals, long division and dice rolls, all built from eight punctuation marks"
author: "Penny Quirke"
published: 2026-09-26T08:48:36.035Z
modified: 2026-09-26T19:05:35Z
url: https://rews.cc/a/a-200-line-ray-tracer-written-in-brainfuck-a-language-with-e-527151
language: en
tags: ["programming", "brainfuck", "ray-tracer", "llm", "compiler", "tech"]
publisher: "Rews (https://rews.cc)"
---

# A 200-line ray tracer written in Brainfuck, a language with eight commands

*Fixed-point decimals, long division and dice rolls, all built from eight punctuation marks*

By Penny Quirke · September 26, 2026 · https://rews.cc/a/a-200-line-ray-tracer-written-in-brainfuck-a-language-with-e-527151

## In brief

- Programmer mTvare6 wrote a ray tracer in Brainfuck, a language of eight punctuation marks and an infinite tape of byte cells
- Rules: no looking up existing implementations, everything from first principles, targeting the sample image from Ray Tracing in One Weekend
- Decimals were spread across cells in a signed Q8.8 fixed-point layout with an invisible binary point
- An LLM converted C into SSA-like style with Hungarian prefixes before a home-made tool generated the Brainfuck
- Random numbers for anti-aliasing use A = (5A + 1) mod 256, cycling through all 256 values before repeating

The official CMake tutorial contains a confession. Buried in its guidance on keeping your build system sane, it admits that “even ray-tracers have been written in CMake Language, but this is not a recommended practice.” To most readers that is a warning. To one programmer, relearning CMake for a C++ systems competition after years of being spoilt by Rust’s Cargo, it read more like a bat signal.

The programmer, who publishes as mTvare6, had already written one ray tracer and then re-written it for the GPU, a project that had leaned on a fiddly stack of APIs rather than first principles. So this time they reached for the simplest language they knew: BF, better known as Brainfuck, on the logic that a simple language obviously results in a very simple codebase. A remark in the project README from someone named Muller added fuel: a counterexample suddenly felt necessary. The result lives on GitHub as mTvare6/rayfuck.

Brainfuck is a language stripped to eight punctuation marks and one piece of imaginary hardware: an infinite tape of cells, each holding a single byte. The characters > and \< slide a pointer right and left along the tape; + and - raise or lower the value in whatever cell the pointer is visiting; , reads a byte of input and . prints one; and the brackets \[ \] form the only loop, which spins as long as the current cell is not zero. That is the whole language. There is no addition, no multiplication, and no such thing as a named variable, yet this is enough to be Turing-complete. As a warm-up, the author suggests writing a cat program in just five characters, and leaves it as homework. So do I.

The ground rules were austere: no looking up existing implementations, everything derived from first principles, and one clear destination — the exact sample image rendered in the Metal chapter of *Ray Tracing in One Weekend*. Translating the C version directly was judged too messy, and writing a C parser was out of the question. “Writing an unmaintained C parser is something better handled by Anthropic,” the author notes.

The first problem is that ray tracers run on decimals and Brainfuck runs on single bytes. The author’s fix was to spread each number across several cells with an invisible binary point parked in the middle — a scheme they later learned is called Q format. A thrifty signed Q8.8 layout would resolve steps of 1/256 but top out at about 128, and the scene’s ground is a sphere with a radius of 1,000, large enough to pass for flat. So the budget went up to signed Q16.16: resolution of 1/2^16 and a range from -32,768 up to 32,768, an exclusive club. Enough.

The actual writing happens several floors above Brainfuck. An LLM was given exactly one job: converting the C into an SSA-like style, recursion ironed into loops and every variable wearing a Hungarian-style prefix so no two names collide when tape addresses are assigned. From there the code passes through a home-made intermediate language of 28 operations — abs, add, and, call, copy, div, else, end, eq, func, ge, gt, if, int, le, lt, mul, neg, not, or, print2, print3, set, sqrt, sub, text, var and while — which a Python code generator then flattens into BF.

Even the luxuries got rebuilt from scraps. Random numbers, needed for supersampling anti-aliasing, come from A = (5A + 1) mod 256, chosen after fancier candidates showed poor periods; this one cycles through all 256 values before repeating, which the author judges good enough for jittering pixel samples.

Square roots took four auditions. Heron’s method — which the author had just re-encountered in that same CMake tutorial, because the universe enjoys a callback — requires division, and division is dear. Repeated subtraction generated compact code but cost too much to run. A Taylor series fit beautifully until it didn’t: below an encoded value of 20,000, roughly 0.305, the approximation wandered off, and that was “a pretty important region.” So the role went to the school method: literal long division, the kind with the little staircase. Since numbers are stored as N = x · 2^16, the trick is that isqrt(2^16 · N) lands directly on the correctly encoded answer, and being off by one in that encoding moves the true square root by less than 1/2^16 — about 0.00001526.

Underneath everything, the generated code leans on two gestures. “Move,” which turns \[a, 0\] into \[0, a\], is the one-liner \[->+\<\]: loop, decrement here, step right, increment, step back, repeat until empty. “Copy” takes \[a, 0, 0\] to \[0, a, a\] with \[->+>+\<\<\], and the spare a can be shuffled home afterwards. A dictionary maps every variable to its tape address, and because addition and division churn through scratch values, each variable gets temporary slots nearby — carry cells included — so the pointer never has to commute.

Multiplication is repeated addition wearing a trench coat: one operand is copied off to serve as the outer loop counter, the other is re-added once per pass. With four-cell values, every cell of one factor is paired with every cell of the other, the product of cells i and j landing at position i+j of an eight-cell result. And since both inputs already carry a 2^16 scale factor, the bottom two cells of the result are simply discarded when it is copied back — the divide-by-2^16 that puts the binary point back where it belongs.

Division, fittingly, really is long division. Where schoolchildren carry remainders in base ten, this version reads the dividend one cell at a time and carries in base 256: remainder times 256 plus the next cell, then subtract the divisor until the remainder goes negative, undo the last step, write the tally of subtractions into the quotient — at most 255 of them per cell — and move on. Because the leftward shifts would otherwise throw precision away, the dividend must first be inflated by 2^16.

Over at Hacker News, commenters arrived at the obvious objection. “Isn’t this just a ray tracer in python/c that spits out brainfuck?” asked one, throwaway99e2. “By this logic gcc writes all my programs in assembly lol.” Another, shoo, supplied the retort: in Brainfuck “the language doesn’t have variables, so you need to manually do the bookkeeping of which memory offset is storing what ‘variable’,” and change the layout and you get to re-derive every offset by hand — writing it directly would be “neither a productive nor interesting exercise.” Letting a machine do the bookkeeping is, after all, the entire point of machines.

As for the promise that a simple language yields a simple codebase: BF programs, the author observes, “regularly tend to be only a few lines long.” The ray tracer honors the tradition at about 200 lines. One suspects the lines run long.
