DOOM Source Release: The Portable Engine That Outlived Its Own Machine
Inside `linuxdoom-1.10`, the split between system code and game code, the 35 Hz heartbeat, and the fixed-point tricks that made a 1993 shooter a permanent portability benchmark.
- DOOM became portable because id split the engine into a thin system layer and a durable core that could survive platform changes.
- Its 35 Hz tic loop made simulation deterministic, which is why demos and multiplayer could reuse the same command stream.
- Fixed-point math and linked thinker lists let a 1993 shooter stay fast on weak hardware without collapsing into complexity.
- The Linux source release did more than preserve a game, it created the template for decades of source ports that still argue over fidelity versus extension.
The surprise in the `linuxdoom-1.10` tree is not that it runs a famous game. It is that the code was already organized like a portability kit. The platform-facing pieces live in the `i_` files, while the game’s logic, physics, and rendering sit behind them like a core that can be carried to a new machine with surprisingly little surgery.
Here it is, at long last. The DOOM source code is released for your non-profit use. You still need real DOOM data to work with this code. If you don't actually own a real copy of one of the DOOMs, you should still be able to find them at software stores.
A Game Engine Built to Be Moved
The source release matters because it exposed an engine that was designed for reuse before “engine” was a fashionable word in games. `i_main.c` is basically a wrapper around `D_DoomMain()`. That is the tell. The executable starts in the platform layer, hands off immediately, and lets the rest of the program pretend the machine beneath it is just another replaceable part.
The `i_` Files Are the Secret
The architecture split is almost comically elegant. The `i_` files handle video, sound, input, and OS glue. Everything else assumes those chores are someone else’s problem. That boundary made the Linux release valuable to porters because they only had to rewrite the shell, not reassemble the brain.
int main(int argc, char **argv)
{
D_DoomMain();
return 0;
}
That tiny handoff is the whole story in miniature. The code is not modular in the abstract, it is modular in the way that matters under deadline and on weak hardware. Replace the interface layer, keep the simulation stable, and the game still behaves like DOOM.
35 Hz: The Game’s True Pulse
Modern engines often let rendering define the rhythm. DOOM does the opposite. Its heartbeat is the 35 Hz tic rate, and the core loop advances one simulation step at a time. That choice makes the game feel locked, repeatable, and exact, which is why demo playback and multiplayer can reuse the same command stream instead of trying to reconstruct intent from rendered frames.
| DOOM tic loop | Modern frame-driven engine |
|---|---|
| Simulation advances on fixed tics | Simulation often follows render cadence |
| Input becomes a command record | Input is usually sampled per frame |
| Demo playback can replay the same commands | Playback often needs special capture logic |
| Network play stays deterministic when rules match | Network code may compensate for variable frame timing |
The point is not nostalgia. The point is that fixed timing turns a game into a machine that can be reproduced. If you can replay the same commands, you can study, port, speedrun, and debug the same behavior across wildly different systems.
How DOOM Cheats Physics Without Feeling Cheap
The code cheats in the best possible way. It uses fixed-point math instead of floating point, which mattered on machines that could not afford expensive arithmetic. The world is also “2.5D” rather than fully 3D. Floors, ceilings, and heights create the illusion of space, while the underlying simulation stays compact enough to move fast.
| DOOM’s approach | What that buys |
|---|---|
| Fixed-point arithmetic | Predictable speed on weak CPUs |
| Thinker list for active objects | Fast iteration over monsters and projectiles |
| 2.5D level model | A convincing 3D feel with simpler math |
| Deterministic state updates | Reliable demos, replays, and ports |
That is the broader pattern across the codebase. DOOM avoids expensive generality wherever it can. It keeps the moving pieces small, explicit, and easy to replay. The result is not a toy engine. It is a brutally efficient one.
Why the Linux Release Became a Source-Port Factory
We couldn't release the dos code because of a copyrighted sound library we used (wow, was that a mistake -- I write my own sound code now)
The Linux release was not a compromise in spirit. It was the codebase most legally available to publish, and it still contained the essential architecture. That was enough. Once people had the engine, they could retarget it, modernize it, and disagree about how much of the original experience should survive.
| Port style | What it tries to preserve | What it adds |
|---|---|---|
| Chocolate Doom | Original timing, limits, feel | Very little beyond faithful execution |
| Crispy Doom | Vanilla behavior | Higher resolutions, widescreen, limit removal |
| GZDoom | The Doom lineage | Hardware acceleration, scripting, major feature growth |
That spectrum is the legacy of the release. The original code became a reference point, then a bargaining chip, then a shared language. Every port has to answer the same question: are you preserving DOOM, or extending it?
What Every Modern Port Is Really Arguing About
The source-port ecosystem is not just a family tree. It is a long-running debate about boundaries. Purist ports defend the original machine. Feature-rich ports treat the engine as a living substrate. The source release made both positions possible, which is why the code still matters long after the original hardware vanished.