MiniDoom is a full MiniLang port of the original DOOM engine codebase, focused on gameplay parity, classic behavior, and native Windows/Linux x64 execution.
This project keeps the original DOOM architecture and module split concept, but translates the implementation to MiniLang (.ml) with platform-specific runtime bindings where needed (video, audio, input, window handling).
- Native x64 builds for Windows and Linux from the same MiniLang source tree.
- Original software renderer plus an accelerated OpenGL renderer, switchable at runtime with
Alt+G. - DOOM and DOOM II IWAD/PWAD support, savegames, demos, menus, HUD, sound effects, and MUS music.
- Built-in drop-down console with classic cheats and developer commands.
- Host-authoritative UDP multiplayer for up to four players in cooperative and deathmatch modes.
- Automatic 3× HDWAD rendering cache for OpenGL textures, sprites, UI graphics, and level geometry.
| Feature | Windows x64 | Linux x64 |
|---|---|---|
| Classic software renderer | Win32/GDI presentation | SDL2/OpenGL presentation |
| Accelerated renderer | OpenGL compatibility profile | SDL2/OpenGL compatibility profile |
| Sound effects | WinMM PCM | SDL2 queued audio |
| MUS music | Windows MIDI mapper | FluidSynth + MuseScore General Lite |
| Input and windowing | Win32 | SDL2 |
| Multiplayer | UDP/WinSock | UDP/POSIX sockets |
Prebuilt Windows and Linux packages are available on the GitHub Releases page. The packages contain the engine only; copyrighted DOOM IWADs are never included.
Windows:
.\MiniDoom.exe -iwad "C:\Games\DOOM\DOOM2.WAD" -windowed -openglLinux (install libsdl2-2.0-0 and libgl1 first):
./run-minidoom -iwad "$HOME/games/doom/DOOM2.WAD" -windowed -openglThe first OpenGL start creates <IWAD>.hdwad next to the selected IWAD. This
one-time build can take a while and requires several hundred megabytes of free
space; later starts reuse the validated cache.
All 59 levels available in the locally tested registered DOOM IWAD (27) and DOOM II (32) are captured from the same player start in both renderers. Classic is shown on the left, OpenGL on the right. The IWAD files are not part of this repository.
DOOM II MAP01 — Classic (left) and OpenGL (right)
DOOM II — MAP01–MAP11
| Level | Classic | OpenGL |
|---|---|---|
| MAP01 | ![]() | ![]() |
| MAP02 | ![]() | ![]() |
| MAP03 | ![]() | ![]() |
| MAP04 | ![]() | ![]() |
| MAP05 | ![]() | ![]() |
| MAP06 | ![]() | ![]() |
| MAP07 | ![]() | ![]() |
| MAP08 | ![]() | ![]() |
| MAP09 | ![]() | ![]() |
| MAP10 | ![]() | ![]() |
| MAP11 | ![]() | ![]() |
DOOM II — MAP21–MAP32
| Level | Classic | OpenGL |
|---|---|---|
| MAP21 | ![]() | ![]() |
| MAP22 | ![]() | ![]() |
| MAP23 | ![]() | ![]() |
| MAP24 | ![]() | ![]() |
| MAP25 | ![]() | ![]() |
| MAP26 | ![]() | ![]() |
| MAP27 | ![]() | ![]() |
| MAP28 | ![]() | ![]() |
| MAP29 | ![]() | ![]() |
| MAP30 | ![]() | ![]() |
| MAP31 | ![]() | ![]() |
| MAP32 | ![]() | ![]() |
Regenerate the gallery from local IWADs with:
.\tools\capture_readme_gallery.ps1- Port original DOOM engine logic to MiniLang as faithfully as possible.
- Preserve classic gameplay behavior (movement, combat, AI, doors/switches/triggers, HUD/menu flow).
- Keep rendering semantics close to the original pipeline (BSP, walls, visplanes, sprites, clipping).
- Run as a native Windows x64 executable or Linux x64 ELF built with the MiniLang compiler.
- The original C/H codebase was mapped module-by-module to MiniLang.
- In most cases, one gameplay/render/system C module is represented by one MiniLang file.
- Data structures (
struct, enums, tables, globals) were ported explicitly. - Platform services use Win32 on Windows and a compact SDL2/OpenGL bridge on Linux.
- The Python build flow selects the correct native helpers and handles Windows EXE icon injection.
MiniDoom/
src/ # MiniLang game/engine source files and platform bindings
docs/api/ # Generated MiniDoc HTML and Markdown reference
docs/gallery/ # Classic/OpenGL level comparison screenshots
icons/ # PNG + ICO assets for EXE icon resources
tests/ # Smoke, renderer, console, multiplayer, and regression tests
tools/
capture_readme_gallery.ps1 # Regenerates the README screenshot gallery
check_source_comments.ps1 # Audits MiniDoc declaration documentation
generate_minidoc.ps1 # Checks or regenerates the API reference
exe_icon_injector.ml # MiniLang tool: injects .ico into Windows .exe resources
minidoom_gl_helper.c # Cross-platform accelerated rendering helper
minidoom_linux_platform.c # Linux SDL2 window/input/audio bridge
build.py # Windows/Linux native build orchestrator
minidoc.toml # Strict MiniDoc project configuration
THIRD_PARTY_NOTICES.md # Notices for bundled Linux audio components
LICENSE
README.md
- Windows x64 or Linux x64
- Python 3.10+ (recommended: 3.11+)
- MiniLang compiler (Python implementation):
MiniLangCompilerPy - Compiler version 1.1.0 or newer with
windows-x64andlinux-x64targets - Windows builds: MSVC x64 build tools and the Windows 10/11 SDK
- Linux builds: GCC, the SDL2 runtime (
libSDL2-2.0.so.0), OpenGL (libGL.so.1), FluidSynth (libfluidsynth.so.3), and a GM SoundFont - A legally obtained DOOM IWAD such as
DOOM.WAD,DOOM1.WAD, orDOOM2.WAD
IWAD, PWAD, generated HDWAD, configuration, and save files are not shipped in the repository or release archives.
MiniDoom uses MiniDoc's native //! file comments and /// declaration
comments, including parameter and return contracts. The generated reference is
available as repository-friendly Markdown and
as an offline HTML site at docs/api/html/index.html.
Regenerate both formats with a current MiniDoc and MiniLang compiler checkout:
pwsh .\tools\generate_minidoc.ps1 `
-MiniDoc "C:\path\to\MiniDoc\build\minidoc.exe" `
-CompilerRoot "C:\path\to\MiniLangCompilerPy"Use -Check to perform the same strict documentation validation without
rewriting generated files.
Use build.py from this repository root.
python .\build.py `
--compiler "C:\path\to\MiniLangCompilerPy\mlc_win64.py" `
--std "C:\path\to\MiniLangCompilerPy\std" `
--target windows-x64What this does:
- Builds
MiniDoomGL.dll - Compiles
tools/exe_icon_injector.ml - Compiles
src/i_main.mltobuild/MiniDoom.exe - Injects
icons/MiniDoom.icointo the executable
Final output:
build/MiniDoom.exe
build/MiniDoomGL.dll
On Linux, install GCC plus the SDL2 and OpenGL runtime libraries, then run:
sudo apt install gcc libsdl2-2.0-0 libgl1 libfluidsynth3 musescore-general-soundfont-smallpython3 ./build.py \
--compiler /path/to/MiniLangCompilerPy/mlc_win64.py \
--std /path/to/MiniLangCompilerPy/std \
--target linux-x64 \
--cleanThe same Linux build can be cross-built from Windows when WSL with GCC is available:
python .\build.py `
--compiler "C:\path\to\MiniLangCompilerPy\mlc_win64.py" `
--std "C:\path\to\MiniLangCompilerPy\std" `
--target linux-x64 `
--cleanLinux output is placed in build/linux/:
build/linux/MiniDoom
build/linux/run-minidoom
build/linux/libMiniDoomPlatform.so
build/linux/libMiniDoomGL.so
Use run-minidoom; it sets the local shared-library search path before
starting the ELF. Both the classic and OpenGL renderers, keyboard/mouse input,
SDL2 sound effects, screenshots, saves, and UDP multiplayer use native Linux
services. Doom's MUS tracks play through FluidSynth. The prebuilt release
includes FluidSynth and the MIT-licensed MuseScore General Lite SoundFont, so
it needs no MIDI daemon or extra SoundFont installation.
For a source build, install libfluidsynth.so.3 and place a compatible GM
SoundFont beside the executable as MiniDoom.sf3/MiniDoom.sf2, or select one
with MINIDOOM_SOUNDFONT=/path/to/soundfont.sf3. Release builders can copy a
self-contained runtime into the output directory with:
python3 ./build.py \
--compiler /path/to/MiniLangCompilerPy/mlc_win64.py \
--std /path/to/MiniLangCompilerPy/std \
--target linux-x64 \
--linux-music-runtime /path/to/runtime \
--cleanThe runtime directory must contain libfluidsynth.so.3,
libinstpatch-1.0.so.2, and MiniDoom.sf3. See
THIRD_PARTY_NOTICES.md for licenses and
attribution.
--output-dir <path>: change output directory--target windows-x64|linux-x64: choose PE or ELF output--skip-icon: build without icon injection--clean: remove output directory before build--icon <path.ico>: use a custom icon file--icon-group <id>/--icon-lang <id>: resource ids for icon injection--skip-gl-helper: reuse an existing WindowsMiniDoomGL.dllwithout rebuilding it (Linux always builds its helpers)--linux-music-runtime <path>: bundle FluidSynth, libinstpatch, andMiniDoom.sf3in a Linux build
Compile directly via MiniLang compiler:
python C:\path\to\mlc_win64.py `
.\src\i_main.ml `
.\MiniDoom.exe `
-I .\src `
-I C:\path\to\MiniLangCompilerPy `
--target windows-x64 `
--subsystem windowsIf you want the EXE icon embedded, build and run the icon injector:
python C:\path\to\mlc_win64.py `
.\tools\exe_icon_injector.ml `
.\exe_icon_injector.exe `
-I .\src `
-I C:\path\to\MiniLangCompilerPy `
--target windows-x64 `
--subsystem console
.\exe_icon_injector.exe .\MiniDoom.exe .\icons\MiniDoom.icoExample run:
.\build\MiniDoom.exe -iwad "C:\Games\DOOM\DOOM2.WAD"Linux:
./build/linux/run-minidoom -iwad "$HOME/games/doom/DOOM2.WAD"If no -iwad is provided, the engine uses its internal IWAD search order and loads the first matching file it finds.
The classic renderer remains the default. Start the accelerated 3D renderer with:
.\build\MiniDoom.exe -iwad "C:\Games\DOOM\DOOM2.WAD" -windowed -opengl./build/linux/run-minidoom -iwad "$HOME/games/doom/DOOM2.WAD" -windowed -openglPress Alt+G while running to switch between OpenGL and the classic pixel
renderer. OpenGL uses VSync by default to avoid tearing and uneven presentation.
Runtime options:
-novsync: disable VSync (useful for profiling)-vsync: explicitly enable VSync-maxfps <0..1000>: apply an additional frame-rate cap;0disables that cap-profile-render: write per-second frame percentiles and renderer-stage timings tominidoom_profile.log
If the graphics driver does not expose swap-interval control, MiniDoom falls back to a 60 FPS high-resolution limiter.
Open or close the in-game console with ~, Ö, or ^ (depending on the active keyboard layout; German AltGr++ is also supported). The console slides over the upper third of the screen, pauses the current single-player game, and captures all movement and fire input while it is open. HUD notices, multiplayer chat, errors, and command results are mirrored into its scrollback.
Console controls:
Enter: execute the current command.Up/Down: navigate command history.Page Up/Page Down: scroll the log.Escape: close the console.
Available commands:
| Command | Effect |
|---|---|
help |
Show console controls and utility commands. |
cheats |
List all gameplay cheats. |
iddqd |
Toggle god mode. |
idkfa |
Give all weapons, full ammo, armor, and all keys. |
idfa |
Give all weapons, full ammo, and armor without keys. |
idclip |
Toggle wall collision. |
idclev <NN> |
Warp to MAPNN, or to episode/map ExMy for episodic DOOM. |
invisible |
Toggle persistent notarget mode; monsters do not react to the player. |
freeze |
Freeze monsters, animations, and world specials while leaving player movement active. |
kill monsters |
Kill all active monsters in the current level. |
name [Player Name] |
Show or change the local player name. Active multiplayer sessions receive the change immediately. |
fps |
Toggle the on-screen FPS display. |
clear |
Clear console scrollback. |
quit |
Exit MiniDoom. |
Gameplay-changing cheats are single-player only to prevent multiplayer desynchronization. The original quick-warp form also works without opening the console: type idclev12 during normal play for DOOM II MAP12, or idclev23 for DOOM E2M3. These typed cheat keys do not block ordinary movement controls.
The OpenGL renderer uses a generated sidecar cache next to the selected IWAD:
DOOM2.WAD
DOOM2.WAD.hdwad
When -opengl is requested and no valid sidecar exists, MiniDoom builds it
automatically. The cache contains palette-aware 3× wall textures, flats,
sprites, HUD/menu graphics, fonts, full-screen patches, and precomputed OpenGL
geometry for every discovered map. A loading screen reports the current phase,
percentage, and estimated remaining time.
Useful options:
-rebuildhdwad: regenerate the automatic cache even when a valid one exists.-hdwad <path>: load an explicitly selected HDWAD instead of auto-generating one.-nohdwad: disable automatic cache attachment/generation for diagnostics.
The generated file can be several hundred megabytes. It is renderer-only, excluded from multiplayer WAD fingerprints, and can be deleted safely; the next OpenGL start will recreate it. Classic rendering does not require an HDWAD.
MiniDoom includes an in-game UDP multiplayer mode with host-authoritative simulation.
- Up to 4 players total (slots 0..3).
- Modes:
CoopandDeathmatch. - Host-configurable map, skill, max players, frag limit, and time limit.
- Player names (max 25 characters), including host-authoritative runtime renaming through
name <Player Name>in the console. - HUD join/leave/kill messages and chat relay.
- Intermission/state synchronization between peers.
- Open
Multiplayerfrom the main menu. - Choose one of:
Host GameJoin GamePlayer Name
In Host Game, configure:
Mode(COOP/DEATHMATCH)MapSkillMax PlayersFrag Limit(0= unlimited)Time Limit(0= unlimited)Port(default:2342)
Then select Start Host.
In Join Game, set:
Host(numeric IPv4 address, for example127.0.0.1)Port(must match host)
Then select Join.
The command-line path uses the same host/join handshake and game bootstrap as the menus. Start a host with:
.\build\MiniDoom.exe -iwad "C:\Games\DOOM\DOOM2.WAD" -mp-host 2342 -mp-mode coop -mp-map MAP01 -mp-skill 2 -mp-maxplayers 4 -mp-fraglimit 0 -mp-timelimit 0 -mp-name HostJoin it from another process or machine with:
.\build\MiniDoom.exe -iwad "C:\Games\DOOM\DOOM2.WAD" -mp-join 127.0.0.1 2342 -mp-name MarineAvailable options:
-mp-host <port>: host a session on UDP port1..65535.-mp-join <numeric-ipv4> <port>: join a host; DNS hostnames are not currently accepted.-mp-mode <coop|deathmatch>: select the host game mode.-mp-map <MAP01|E1M1>: select a map present in the loaded WAD set.-mp-skill <0..4>: select the host skill from baby through nightmare.-mp-maxplayers <2..4>: cap total active slots, including the host.-mp-fraglimit <0..999>and-mp-timelimit <0..180>: configure deathmatch limits; zero disables a limit.-mp-name <name>: set the local sanitized player name (up to 25 characters).-mp-log <path>: write machine-readable connection, slot, map, and disconnect status lines to a per-process log. Runtime continues on stdout if the file cannot be opened.
On host and client startup for multiplayer, MiniDoom computes a load-order-sensitive fingerprint (FNV-1a) over the IWAD and all gameplay PWADs. Optional generated .hdwad rendering caches are excluded.
Join is rejected if fingerprints do not match.
Practical recommendation: all players should use the same IWAD/PWAD files in the same load order.
- Press
Tin-game to open chat input. - Send with
Enter. - Messages are relayed host-authoritatively and shown as:
<PlayerName>: <message>
- Use
name <Player Name>in the drop-down console to rename yourself while connected. The host validates the sender slot and distributes the refreshed name table to every client, including late joiners.
Player names accept ASCII letters, digits, spaces, hyphens, and underscores. Leading/trailing spaces are removed and names are limited to 25 characters.
- Transport: UDP.
- Server-authoritative world state.
- Clients send inputs; host simulates the world and sends snapshots/events.
Host did not respond (timeout): verify host address/port and firewall/NAT rules.WAD fingerprint mismatch: ensure all peers use the same IWAD.Server full: lower active players or increase max players (up to 4).
The PowerShell test harness covers software/OpenGL rendering, renderer toggling, sprites, wall offsets, moving geometry, sky behavior, wipes, console commands, frame pacing, invisibility, HDWAD generation, and multiplayer transport/loopback.
pwsh .\tests\run_tests.ps1 `
-Compiler "C:\path\to\MiniLangCompilerPy\mlc_win64.py" `
-Std "C:\path\to\MiniLangCompilerPy\std" `
-Iwad "C:\Games\DOOM\DOOM2.WAD"
pwsh .\tools\check_source_comments.ps1 -Summary- Core engine behavior targets original DOOM parity while using MiniLang runtime semantics.
- Platform services are adapted to native Win32 and Linux SDL2/OpenGL execution.
- Build and tooling are modernized (single Python build script + MiniLang resource tool).
See LICENSE.






















































































































