
Play
A PDF file can run Doom, and that is the boring version
Doom runs inside a PDF file and glows through a gutted pregnancy test. Why the 1993 engine never stops finding stranger machines to draw it.
Somewhere on your machine right now sits a program that can run Doom, and it is not a game console. It is your PDF reader.
doompdf is the 1993 shooter running inside a single PDF file, playing in the same browser tab you open receipts in. You type into a text box to move, the corridors redraw as rows of ASCII characters in six shades of grey, and each frame takes about 80 milliseconds. It is slow, and it is unmistakably Doom.
The repo went up in January 2025 and sits at 3,870 stars, which for context is more than most production database tools will ever collect. People know a landmark when they see one.
How a document runs a video game
The PDF format has carried its own JavaScript engine for decades, a leftover from Adobe's dream of interactive paperwork, and browser PDF viewers implement a slice of that standard library to run form logic. That slice is the whole computer here.
The port, by a developer who goes by ading2210, starts from doomgeneric, a version of the engine maintained specifically to be carried to strange places, and compiles the original C with an old build of Emscripten that still emits asm.js, the highly optimizable JavaScript dialect from before WebAssembly existed, because a PDF cannot load anything newer. There is no canvas to draw on, so the port writes each screen row into a separate text field and swaps the characters every frame, one ASCII shade per brightness level. Those 80-millisecond frames pencil out to twelve and a half frames a second, in a file format designed for invoices.
Why the museum-piece compiler? Because the JavaScript engine inside a PDF viewer predates the modern web platform. There is no WebAssembly loader in there, no typed-array fast path you can count on, just a dialect old enough that asm.js, the 2013 trick of writing machine-like JavaScript a compiler can trust, is the newest thing it will swallow. The port is not nostalgic. It is cornered.
The runtime really is that cramped. The PDF flavor of JavaScript gets no network access and nothing 3D, so the port ships its own scrolling text console for debugging, takes custom WAD files through a build page, and does every last bit of its rendering as strings. The whole thing is playable in your browser right now. The game loop was always there. Someone just found a stranger place to keep it.
Why it is always Doom
The reason is the code itself. id Software released the source in December 1997 and relicensed it under the GPL in 1999, and that engine was built to run with no graphics card and no floating-point unit, on a software renderer doing fixed-point math. The famous trick inside is the BSP tree, the level pre-sliced at build time so the renderer always knows what is in front of what without doing real 3D. Give it a few hundred kilobytes of memory and something shaped like a screen and it will draw a level. That is a very low bar, and thirty years of tinkerers have spent their weekends finding things that clear it.
There is a second ingredient people forget: the 1997 source release arrived just as the modding community was already a culture, with years of custom WADs behind it. The code was portable, the audience was primed, and the phrase "but can it run Doom" fossilized into a benchmark the way "hello world" did, except this benchmark shoots back.
The pregnancy test, with receipts
The pregnancy test is the honest edge of the joke, and the teardown is the receipt. When Foone Turing opened one up, the guts turned out to be an 8-bit Holtek microcontroller with 64 bytes of RAM, 1,024 words of ROM, and a screen that can show a few fixed glyphs, all of it there to read a paper strip that does the actual chemistry. Nothing in that list is reprogrammable. Nothing in that list will ever draw a corridor.
Hold the 64 bytes up against the game's own box: Doom asked for 4 megabytes of RAM in 1993, which is 4,194,304 bytes, or sixty-five-thousand times what the test's chip carries. The gap is not a port away. The gap is a different century of machine.
So the shell got a transplant: an Adafruit Trinket M0 and a 0.91 inch, 128 by 32 pixel monochrome OLED. Even then, Doom itself runs on a PC nearby; the game is dithered down to one bit per pixel and streamed to the tiny screen. Do the arithmetic and a full frame is 512 bytes, a trickle the little microcontroller sips without effort. The pregnancy test did not run Doom. The pregnancy test became a tiny second monitor for a Doom running somewhere saner, which is a different claim and a funnier one, and the people who build these are careful about the difference.
That care is the whole hobby. "Will it run Doom" is a real benchmark because the answer is almost always yes once you say what "it" means, and the good entries show their work: the transplant, the frame time, the compile target.
The leaderboard keeps getting stranger
Once I started pulling entries for this piece I could not stop, so here are the three that broke my scale for what counts as a computer.
The slowest Doom ever measured runs in TypeScript's type system. Dimitri Mitropoulos, founder of Michigan TypeScript, spent a year and a half building a virtual machine out of types, implementing all 116 WebAssembly instructions the game needs as type-level logic, then let the type checker execute it. The checker chewed through 177 terabytes of types at a sustained 20 million type instantiations per second, and after 12 days it had rendered the first frame. One frame. The compiler is not a runtime, and he made it one anyway, with the frame time to prove it.
The slowest display belongs to biology. MIT biotech student Lauren Ramlan built a 32 by 48 pixel screen out of E. coli bacteria, 1,536 wells where each cell either fluoresces or does not, one living bit per pixel. A frame takes about 70 minutes to light up, the reset back to dark takes eight hours and twenty minutes, and by Ramlan's own arithmetic a full playthrough at that cadence lands near 600 years. Like the pregnancy test, the bacteria are the monitor, not the machine, and the writeup says so plainly.
And the most useful entry, if that word applies anywhere here, is the Doom CAPTCHA, which Vercel's Guillermo Rauch wired up as an actual prove-you-are-human gate: kill three monsters on E1M1, on Nightmare difficulty with fast monsters, using arrow keys and a spacebar. It is the same shareware WAD and the same Emscripten compile-to-the-browser trick as the PDF port, pointed at form security instead of paperwork.
The integration is tidier than it has any right to be. The C side exposes callbacks, onPlayerBorn, onPlayerKilled, onEnemyKilled, and the page just counts the third one up to three. Dying respawns you automatically so the gate never strands a bad shot. Somewhere a product manager is reading this and opening a ticket.
Three entries, three different claims: Doom as proof a type checker is Turing-complete in practice, Doom as proof a petri dish is a display, Doom as proof you are not a robot. The engine did not change. The definition of "machine" did.
Port one yourself
The trailhead is doomgeneric, the same base doompdf forked. Implement five functions, init, draw frame, read a key, sleep, and read the clock, and the engine will paint its first frame on whatever you own that resembles a display. The example ports in the repo cover Windows, X11, SDL and the browser, and the pattern generalizes to sillier targets from there.
The house rules of the hobby, as practiced by the entries above: say what the machine actually is, publish the frame time, and admit when the weird thing is only the display. Do those three and your port joins a thirty-year lineage. Skip them and the forum will do the teardown for you.
Browsers have quietly become the natural habitat for this kind of transplant; this tab has hosted heavier organs than a 1993 shooter, including a full Debian Linux booting with no server behind it and every Macintosh since 1984, compiled with the same Emscripten that squeezed Doom into the PDF.
Open the PDF one. Walk one corridor. Then remember it is a document.

Next up
Every one of these drawings is a single HTML div