Bbetlangv0.1.0
why I built bet

56,000 lines of DOOM, in a language I made up.

bet is a small, real programming language: slang keywords on the surface, a serious compiler underneath. It compiles to native code through LLVM, manages its own memory, self-hosts, and runs DOOM. I built it as a contained experiment, and ran it to completion.

I built bet on a break from trying to make something that sells. I wanted a project with a clear edge to it, one I could actually finish, and building a compiler had been on my list for a long time. A joke language gave me the excuse: keywords that read like a group chat, wrapped around a compiler that had to genuinely work. I set one rule at the start: no research. I never checked whether anyone had built this before, because I didn’t want the internet to kill the idea before I’d written a line. As it turned out, Geoffrey Huntley already had, and I didn’t learn that until later.

A language that only works at the level of syntax is really just a costume, so I gave it one real job. Games allocate and release objects constantly, and both manual bookkeeping and garbage collection cost you frames you can’t spare. bet handles that with arenas: allocate into a scope, use it for a frame, drop the whole scope at once. The pattern itself isn’t new. Zig ships an arena allocator that frees everything in one call, and Odin bakes the same arena and temp-allocator idea in for game code. I wanted bet to carry it as a first-class feature with its own keywords, so the language would read as a design of its own rather than a slang reskin of something that already existed.

It’s a joke. I still wanted it to actually do something.

The other reason I built it was to run an experiment. I wanted to see how far an AI coding agent could get on a hard, well-specified project if I stayed out of the implementation. My job was to be the architect: I set the problem and the acceptance criteria, then held the line on them. I did zero code review. A change shipped only if it passed its tests, met the acceptance criteria, and cleared the corpus. That was the only gate.

The proof that it works is DOOM, ported in full. id Software’s original C source for the renderer, the game logic, the WAD loading, and the audio was rewritten in bet: more than 56,000 lines, compiled to a native binary that runs the real shareware game in a window.

Launch it and DOOM plays itself in attract mode. Those clips are the game’s original recorded inputs, replayed through the simulation. Fixed-point math is what makes that a real test: drift by a single step and the player starts walking into walls. bet replays the inputs in lockstep with the reference C build, frame for frame, so the whole simulation comes out bit-for-bit identical. Getting that far with no shortcuts is what separates a toy language from one that can carry real software, and it’s the result I trust most on a build I never code-reviewed.

So it finished, end to end: the compiler, self-hosting, documentation, and this site. And that is where it stops. If a feature request comes in I’ll review it, but there’s no roadmap and no version two on the way. bet was a joke and an experiment and a way to learn what a build like this actually takes, and it did all of that. If there’s one thing I want it to say about me, it’s that I finish what I start.

the part I took seriously

The memory model is the real work.

Manual free() bookkeeping and GC pauses both wreck a frame budget. In bet you allocate into a crib, an arena scoped to the frame, and release the whole thing with evict in O(1). No per-object tracking, no stall in the middle of a frame.

frame.bet
crib wave               // arena for this frame
squad e in spawn(wave) { e.tick() }
evict wave              // whole thing gone, O(1)

That is the experiment. A joke on the surface, one hard problem solved properly underneath, and a build gated by nothing but its tests. It compiles to native code, it self-hosts, and it runs DOOM. It was never meant to become a product, and it doesn't need to be one. It's finished.

how it was built

Where the time actually went.

Real numbers from a commit-tracked timelog, counting active effort with idle time clamped out. That clamping has one gap: three of the agents porting DOOM stalled on usage limits while the clock kept running, so the porting-games row runs about 12 hours high. Take those off and the real total is nearer 22.

porting games
doom, oregon trail, pong
18h
self-hosting the compiler
bet, compiling bet
4h
security review
cwage issues #30–#48
3h
infra, tooling & site
ci, docker, freeze, docs
3h
frontend
lexer · parser · grammar
2h
language & collections
keywords, types, containers
2h
LLVM backend
the actual hard part
1h
arena memory
crib · evict · the point
29m
total≈ 34h tracked· most of it on porting games
prior art · credit where it's due

I didn't know cursed existed until bet was already built. Geoffrey Huntley reached the same idea first: a slang-keyword language, compiled through LLVM, written by directing an AI, the same way I built bet. He did it well, and he got there before me. The credit is his.