OffTheDop
⚠️ Nerd Joke & Tongue-in-Cheek CS Stunt 14 min read Paste & Run Live in ProFacet ↗

ACCIDENTALLY TURING-COMPLETE: WRITING A BRAINFUCK INTERPRETER IN FSL TO CARVE "HELLO WORLD" ONTO A CRYSTAL CUBE

• ProFacet Computational Faceting
Computation Physics & Optics

A quick note: I’m not a trained gemologist. No GIA diploma, no mineralogy degree, and no traditional bench apprenticeship. Just a software engineer who fell down the rabbit hole of faceting machines, geometry, and how light bounces around inside crystals.

There is an unofficial corollary to Greenspun's Tenth Rule that haunts every CAD engineer: give a declarative geometry language first-class closures and array methods so users can parameterize an oval gemstone, and sooner or later someone is going to build a Turing machine inside it.

FSL (Facet Specification Language) was never meant to execute general-purpose bytecode. It is the domain-specific cutting language behind OffTheDop and ProFacet Studio, designed so each line corresponds to setting an index gear tooth, tilting a physical faceting mast, and slicing an infinite flat plane across a 3D crystal polyhedron.

Path-traced Royal Ceylon Sapphire crystal cube displaying HELLO WORLD in frosted Voronoi facets on its top face
Brainfuck HELLO WORLD Crystal Cube (Royal Ceylon Sapphire, 350 convex facets). Zero textures, zero shaders, and zero hardcoded strings: a pure-functional Brainfuck VM written in FSL executes nested-loop Brainfuck bytecode at compile time and cuts 101 frosted Voronoi facets ({ frosted: true }) into the top face of a unit crystal cube.

In fact, to keep gemstone scripts deterministic and browser-safe inside WebAssembly, FSL deliberately omits almost everything a normal interpreter author relies on:

Those four guardrails raise two delightfully unreasonable computer-science questions:

  1. Can you write a pure-functional, Turing-complete Brainfuck virtual machine in FSL that executes tens of thousands of instructions without hitting the 500-frame recursion wall?
  2. How do you carve a 2D pixel framebuffer ("HELLO WORLD") onto one facet of a 3D crystal cube when your only geometric primitive is an infinite flat cutting plane?

Yes on both counts, and the entire working VM, bitmap font, and 350-facet crystal cube fit in 160 lines of standard, unprivileged FSL that you can paste straight into ProFacet Studio right now.

Building A Turing Machine Without Loops, Mutation, Or Tail-call Optimization

Urban Muller's Brainfuck (1993) is the canonical minimalist target for proving accidental Turing-completeness: eight single-character opcodes (>, <, +, -, ., ,, [, ]) operating on a byte tape, a data pointer dp, and an instruction pointer pc.

Implementing Brainfuck in C takes thirty lines of while (*pc) and pointer arithmetic. Implementing it inside a pure-functional CAD evaluator with a 500-frame recursion limit requires three classic functional-programming tricks working together:

Three-stage architectural diagram of the pure-functional FSL Brainfuck virtual machine showing O(L) reduce bracket compilation, persistent copy-on-write tape snapshots, and O(log N) binary function exponentiation
Architecture of the FSL Brainfuck VM. Left: One-pass .reduce() bracket compiler builds a constant-time bidirectional jump table for nested [...] loops using a persistent LIFO stack. Center: Copy-on-write state transition step(vm) updates the 8-bit wrapping tape via .with(). Right: Binary function exponentiation repeatPow2 executes up to 65,536 VM cycles in at most 16 call-stack frames with lazy halt short-circuiting.
  1. One-Pass Persistent Bracket Compilation (buildJumps): Brainfuck's [ and ] loop brackets nest arbitrarily deep. Scanning source text forward and backward on every branch turns inner loops quadratic. Instead, buildJumps folds over the opcode array once in linear time via ops.reduce(...), threading an immutable { stk, jmp } accumulator. Seeing [ pushes pc onto the persistent LIFO stack (st.stk.push(pc)); seeing ] peeks open = st.stk.at(-1), pops the stack, and writes both jump directions in one expression via st.jmp.with(open, pc).with(pc, open). Every loop branch during execution is a constant-time array lookup.
  2. Persistent Copy-on-Write VM Transitions (step(vm)): With mutation outlawed, the entire machine state lives in an immutable five-field map { tape, dp, pc, len, out }. One clock cycle is a pure expression step(vm) returning a fresh map snapshot. Because FSL numbers are IEEE-754 doubles, unsigned 8-bit modular wraparound is expressed branchlessly as (cell + 1) % 256 on + and (cell + 255) % 256 on -, sidestepping negative remainder signs altogether.
  3. Beating the 500-Frame Stack Limit via Binary Function Exponentiation (repeatPow2): Naive linear recursion (run = vm => vm.pc >= vm.len ? vm : run(step(vm))) dies after 500 instructions with a maximum recursion depth error. While a tightly golfed "HELLO WORLD" loop squeaks under the cap in 337 steps, cellular automata and multi-digit decimal arithmetic easily run 15,000 to 30,000 cycles. The fix is to stop recursing linearly over time and instead recurse logarithmically over higher-order function composition:
repeatPow2 = (k, f) => k == 0
  ? f
  : {
      half = repeatPow2(k - 1, f)
      return (vm) => vm.pc >= vm.len ? vm : half(half(vm))
    }

Look closely at what exec = repeatPow2(16, step) constructs. In just 16 calls at setup time, it builds a tower of nested closures where each level applies the level below it twice. At execution time, advancing the Turing machine through up to 65,536 cycles pushes a maximum of 16 stack frames, barely 3% of FSL's call-stack limit.

Even better, the guard vm.pc >= vm.len ? vm : half(half(vm)) makes the binary tree lazy on halt. The instant vm.pc reaches the end of the bytecode, every remaining right-hand subtree short-circuits immediately without descending further. A 337-step program executes 337 transitions and halts in a few milliseconds rather than burning sixty-five thousand no-op cycles. (Parser footnote for compiler nerds: that explicit return keyword before (vm) => is load-bearing. Because FSL does not require semicolons, writing bare (vm) => ... on the line after half = ... causes the Pratt parser to treat (vm) as a function-call argument list invoked on half!)

How Do You Carve 345 Pixels When Every Cut Is An Infinite Flat Plane?

Once msg = runBrainfuck(bfProgram) finishes and prints "HELLO WORLD" at compile time, we face a second puzzle: turning that string into real 3D crystal geometry on the top facet of a cube.

Cutting the five outer walls of a unit crystal cube takes two lines of FSL: G1 0 @ 90.0 : W x4 slices the four vertical 90° walls, and P1 0 @ 0.0 : W x1 slices the flat bottom face.

Now try turning the top face into a 23 by 15 pixel matrix (345 individually addressable facets) so each pixel can be switched between matte microfacet frosting ({ frosted: true } for foreground glyph bits) and mirror-polished sapphire ({ frosted: false } for background bits):

The clean escape comes from computational geometry's paraboloid lifting map (the classic 3D duality between 2D Euclidean Voronoi diagrams and 3D half-space intersections formalized by Edelsbrunner and Seidel in 1986). Suppose we lift each 2D pixel center onto a gentle downward-opening paraboloid of revolution and cut the crystal with the tangent plane to that paraboloid at each pixel center:

Paraboloid surface:   z(x, y)   = W - 0.5 * kappa * (x^2 + y^2)
Tangent plane at i:   T_i(x, y) = z(u_i, v_i) - kappa * u_i * (x - u_i) - kappa * v_i * (y - v_i)
                                = W + 0.5 * kappa * (u_i^2 + v_i^2) - kappa * (u_i * x + v_i * y)

Vertical gap above the paraboloid at any point (x, y):
                      T_i(x, y) - z(x, y) = 0.5 * kappa * ((x - u_i)^2 + (y - v_i)^2)

That three-line identity is pure magic. At every (x, y) coordinate above the cube, the height of each tangent plane above the paraboloid is strictly proportional to the squared 2D Euclidean distance from (x, y) to that pixel's center.

Because half-space crystal cutting retains the lowest plane over every point, the deepest-cutting plane over any point (x, y) is guaranteed to be the pixel site closest to (x, y). Every single one of our 345 tangent planes carves out its own exact rectangular Voronoi cell, and no plane can ever swallow a neighbor:

Cross-section and top-down geometric diagram of paraboloid Voronoi lifting showing tangent planes intersecting along exact pixel midpoints and mapping HELLO WORLD onto 345 convex facets
Left: 2D cross-section of the lifted paraboloid. Tangent planes at uniform pixel pitch intersect strictly along the vertical midlines between pixels, while gentle curvature clears the WebGPU renderer's coplanar deduplication threshold with only 11.25° corner droop. Right: Resulting 23 by 15 Voronoi grid (101 frosted glyph facets, 244 polished facets, and 5 outer cube walls totaling 350 facets).

Running this lifted dome through ProFacet's actual Rust half-space kernel and WebGPU bidirectional path tracer exposed two real engine bounds that pin down the grid dimensions and curvature:

  1. The 512-Facet WebGPU Buffer Ceiling: ProFacet's GPU raytracer packs polyhedron planes into a 512-element buffer. Five outer cube faces plus a 23 by 15 pixel matrix (345 Voronoi cells) totals 350 facets, leaving room for two five-row lines of bitmap glyphs ("HELLO" spans 19 columns; "WORLD" spans 21 columns with the letter W widened so its middle peak stays legible). Notice that we do not even cut a preliminary flat T 0 @ 0 : W top face: the 345 tangent planes close the entire top opening themselves, curving gently by 0.15 units from the center down to the four cube corners.
  2. The Coplanar Slab Deduplicator: To prevent floating-point z-fighting when a gem diagram re-touches an existing plane, the path tracer merges any pair of nearly identical planes whose unit normals sit too close together. Choosing a curvature parameter kappa just above that threshold keeps adjacent pixel normals distinct across the entire 23-column grid so all 350 facets survive without merging, while holding the maximum corner tilt to a subtle 11.25° so the silhouette still looks unmistakably like a crisp crystal cube.

In FSL, slicing each lifted pixel takes one TierAP(tier, idx, ang, Point(u, v, z), "x1", { frosted: on }) call on a 3600-tooth encoder, where ang is the exact surface gradient angle and C1..C345 automatically orient each plane onto the top crown hemisphere.

The Complete Copy-pasteable Fsl Source Code

Everything described above is in the single self-contained FSL file below. Nothing is precomputed offline: when you paste this script into ProFacet Studio (or pass it to fsl on the command line), the evaluator compiles the nested jump table for ++++[>++[>+++++++++>++++>+++++++++++<<<-]<-]>>.---.+++++++..+++.>.>-.<<.+++.------.--------., executes the immutable 64-byte tape machine to produce "HELLO WORLD", verifies stdout via assert(), rasterizes the glyphs, slices all 350 convex half-spaces, and asserts stone().facetCount == 350:

name = "Brainfuck Hello World Cube"
ri   = 1.76
gear = 3600
cube = 1.5

// 1. Pure-Functional Brainfuck VM in FSL (zero loops, zero mutation)
ASCII = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"

buildJumps = (ops) => ops.reduce((st, op, pc) =>
  op == "[" ? st.with("stk", st.stk.push(pc))
  : op == "]" ? {
      open = st.stk.at(-1)
      st.with("stk", st.stk.pop())
        .with("jmp", st.jmp.with(open, pc).with(pc, open))
    }
  : st,
  { stk: [], jmp: ops.map(_ => -1) }
).jmp

// Binary function exponentiation: 2^16 = 65,536 VM steps in <= 16 stack frames
repeatPow2 = (k, f) => k == 0
  ? f
  : {
      half = repeatPow2(k - 1, f)
      return (vm) => vm.pc >= vm.len ? vm : half(half(vm))
    }

runBrainfuck = (src) => {
  ops   = Math.range(0, src.length).map(i => src[i])
  jumps = buildJumps(ops)
  step  = (vm) => {
    op   = ops[vm.pc]
    cell = vm.tape[vm.dp]
    op == ">" ? vm.with("dp", vm.dp + 1).with("pc", vm.pc + 1)
    : op == "<" ? vm.with("dp", vm.dp - 1).with("pc", vm.pc + 1)
    : op == "+" ? vm.with("tape", vm.tape.with(vm.dp, (cell + 1) % 256)).with("pc", vm.pc + 1)
    : op == "-" ? vm.with("tape", vm.tape.with(vm.dp, (cell + 255) % 256)).with("pc", vm.pc + 1)
    : op == "." ? {
        ch = cell == 10 ? "\n" : ASCII[cell - 32]
        vm.with("out", vm.out.push(ch)).with("pc", vm.pc + 1)
      }
    : op == "[" ? vm.with("pc", cell == 0 ? jumps[vm.pc] + 1 : vm.pc + 1)
    : op == "]" ? vm.with("pc", cell != 0 ? jumps[vm.pc] + 1 : vm.pc + 1)
    : vm.with("pc", vm.pc + 1)
  }
  exec = repeatPow2(16, step)
  finalVm = exec({
    tape: Math.range(0, 64).map(_ => 0),
    dp: 0,
    pc: 0,
    len: ops.length,
    out: []
  })
  finalVm.out.join("")
}

bfProgram = "++++[>++[>+++++++++>++++>+++++++++++<<<-]<-]>>.---.+++++++..+++.>.>-.<<.+++.------.--------."
msg = runBrainfuck(bfProgram)
print("Brainfuck stdout:", msg)
assert(msg == "HELLO WORLD", "Brainfuck VM output mismatch")

// 2. Cut the 5 Outer Faces of the Unit Crystal Cube ([-1, 1]^3)
W = 1.0
G1 0 @ 90.0 : W x4
P1 0 @ 0.0  : W x1

// 3. Rasterize Brainfuck Output & Lift 23x15 Voronoi Grid onto Top Cube Face
GLYPHS = {
  " ": ["0", "0", "0", "0", "0"],
  "0": ["111", "101", "101", "101", "111"],
  "1": ["010", "110", "010", "010", "111"],
  "2": ["111", "001", "111", "100", "111"],
  "3": ["111", "001", "111", "001", "111"],
  "4": ["101", "101", "111", "001", "001"],
  "5": ["111", "100", "111", "001", "111"],
  "6": ["111", "100", "111", "101", "111"],
  "7": ["111", "001", "001", "010", "010"],
  "8": ["111", "101", "111", "101", "111"],
  "9": ["111", "101", "111", "001", "111"],
  A: ["010", "101", "111", "101", "101"],
  B: ["110", "101", "110", "101", "110"],
  C: ["011", "100", "100", "100", "011"],
  D: ["110", "101", "101", "101", "110"],
  E: ["111", "100", "111", "100", "111"],
  F: ["111", "100", "110", "100", "100"],
  G: ["011", "100", "101", "101", "011"],
  H: ["101", "101", "111", "101", "101"],
  I: ["111", "010", "010", "010", "111"],
  J: ["001", "001", "001", "101", "010"],
  K: ["101", "101", "110", "101", "101"],
  L: ["100", "100", "100", "100", "111"],
  M: ["10001", "11011", "10101", "10001", "10001"],
  N: ["1001", "1101", "1011", "1001", "1001"],
  O: ["111", "101", "101", "101", "111"],
  P: ["110", "101", "110", "100", "100"],
  Q: ["111", "101", "101", "111", "001"],
  R: ["111", "101", "110", "101", "101"],
  S: ["011", "100", "111", "001", "110"],
  T: ["111", "010", "010", "010", "010"],
  U: ["101", "101", "101", "101", "111"],
  V: ["101", "101", "101", "101", "010"],
  W: ["10001", "10001", "10101", "11011", "10001"],
  X: ["101", "101", "010", "101", "101"],
  Y: ["101", "101", "010", "010", "010"],
  Z: ["111", "001", "010", "100", "111"]
}

renderLine = (txt) => {
  up = txt.toUpperCase()
  Math.range(0, 5).map(r =>
    Math.range(0, up.length).map(i => {
      g = GLYPHS[up[i]]
      g != null ? g[r] : ""
    }).join("0")
  )
}

lines   = msg.includes("\n") ? msg.split("\n") : msg.split(" ")
topRows = renderLine(lines[0])
botRows = lines.length > 1 ? renderLine(lines[1]) : Math.range(0, 5).map(_ => "")

// 23 cols x 15 rows = 345 Voronoi facets (+ 5 cube faces = 350 facets <= 512 GPU cap)
COLS  = 23
ROWS  = 15
zeros = (n) => Math.range(0, Math.max(0, n)).map(_ => "0").join("")
padRow = (bits) => {
  clipped = bits.slice(0, COLS)
  left    = Math.floor((COLS - clipped.length) / 2)
  right   = COLS - clipped.length - left
  concat(zeros(left), clipped, zeros(right))
}

blank  = zeros(COLS)
bitmap = concat(
  [blank, blank],
  topRows.map(padRow),
  [blank],
  botRows.map(padRow),
  [blank, blank]
)

// Paraboloid Voronoi lifting: z(x, y) = W - 0.5 * kappa * (x^2 + y^2)
pitch = (2 * W) / COLS
halfC = (COLS - 1) / 2
halfR = (ROWS - 1) / 2
kappa = 0.175

Math.range(0, ROWS).forEach(row => {
  Math.range(0, COLS).forEach(col => {
    u    = (col - halfC) * pitch
    v    = (halfR - row) * pitch
    r    = Math.sqrt(u * u + v * v)
    ang  = Math.toDegrees(Math.atan(kappa * r))
    psi  = Math.atan2(v, u)
    idx  = (Math.round((-psi / (2 * Math.PI)) * gear) % gear + gear) % gear
    z    = W - 0.5 * kappa * (u * u + v * v)
    on   = bitmap[row][col] == "1"
    tier = concat("C", row * COLS + col + 1)
    TierAP(tier, idx, ang, Point(u, v, z), "x1", { frosted: on })
  })
})

assert(stone().facetCount == 350, "Expected 350 surviving facets")

To make the underlying 3D geometry unmistakably clear, compare the overhead spectral bidirectional path-traced studio render on the left against the raw 469-vertex, 350-polygon convex polyhedron mesh projected directly from ProFacet's half-space engine on the right:

Proving It Is Not A Hardcoded Bitmap: Cellular Automaton Rule 90 And Divmod Fibonacci

Any healthy compiler skeptic looking at a "HELLO WORLD" demo will immediately ask: "Sure, two nested multiplication loops can print eleven ASCII constants, how do I know the VM handles deep bracket nesting, dynamic pointer walks, and multi-thousand-step arithmetic?"

So we kept the exact same runBrainfuck virtual machine and swapped bfProgram for two genuine algorithmic stress tests:

  1. Elementary Cellular Automaton Rule 90 (Sierpinski Gasket, 16,507 VM steps): Adapted from Daniel Cristofani's classic Brainfuck Sierpinski generator (++[>+>++++<<-]>++++++++>>+<[-[>>+<<-]+>>]>+[-<<<[->[+[-]+>++>>>-<<]<[<]>>++++++[<<+++++>>-]+<<++.[-]<<]>.>+[>>]>+]), this program nests five levels of [...] loops and marches its data pointer dynamically across the tape to evolve eight generations of Stephen Wolfram's XOR cellular automaton (Rule 90). Mapping emitted star cells directly to { frosted: true } carves a 27-cell Sierpinski fractal pyramid into the top face of a Colombian Emerald cube.
  2. Iterative Fibonacci and Repeated-Subtraction Base-10 Formatter (29,689 VM steps): Brainfuck has no integer print instruction and no division operator: . emits a single raw byte, so formatting multi-digit integers into ASCII requires computing Fibonacci additions in tape memory and running a nested repeated-subtraction division-by-ten algorithm on every term. The VM prints the first seven Fibonacci numbers across two lines and carves them into the top of a Pigeon-Blood Ruby cube.
Brainfuck Program Bytecode Size Max [...] Nesting Executed VM Steps Max FSL Call-Stack Depth Crystal Material & Frosted Facets
HELLO WORLD 92 bytes 2 levels 337 steps 16 frames (3.2% of cap) Royal Sapphire (RI 1.76), 101 of 350 frosted
Rule-90 Sierpinski 116 bytes 5 levels 16,507 steps 16 frames (3.2% of cap) Colombian Emerald (RI 1.58), 27 of 350 frosted
Fibonacci + divmod 678 bytes 5 levels 29,689 steps 16 frames (3.2% of cap) Pigeon-Blood Ruby (RI 1.76), 92 of 350 frosted

Could you actually cut this 350-facet cube at a physical bench? Technically, every single cut in the script is a flat plane at a real encoder index and mast angle, though unless you own a CNC robotic quill and an unhealthy amount of free time, we strongly recommend keeping your Turing machines inside WebGPU and saving your natural sapphire rough for our 30- to 90-facet Showcase designs.

Copy the FSL listing above, open ProFacet Studio, swap bfProgram for your own Brainfuck code, and watch a gemstone CAD compiler run an 8-bit tape machine onto solid crystal:

Live WebAssembly + WebGPU Compiler · Free in Browser

Paste the Brainfuck-in-FSL Cube Directly into ProFacet Studio

Swap in your own Brainfuck bytecode, watch all 350 convex facets slice in real time on WebGPU, read the FSL Language Reference, or browse gemstones actually meant for human hands on OffTheDop.