MiniDoom is a full MiniLang port of the original DOOM engine codebase, focused on gameplay parity, classic behavior, and native Windows 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).
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 executable (
MiniDoom.exe) 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. - Win32-facing parts (graphics/audio/system) are implemented via native bindings used by MiniLang.
- The build flow is automated with a Python script that also handles EXE icon injection.
MiniDoom/
src/ # MiniLang game/engine source files
docs/gallery/ # Classic/OpenGL level comparison screenshots
icons/ # PNG + ICO assets for EXE icon resources
tools/
capture_readme_gallery.ps1 # Regenerates the README screenshot gallery
exe_icon_injector.ml # MiniLang tool: injects .ico into Windows .exe resources
build.py # Main build script (builds tool + MiniDoom + icon injection)
LICENSE
README.md
- Windows (x64)
- Python 3.10+ (recommended: 3.11+)
- MiniLang compiler (Python implementation):
MiniLangCompilerPy - A DOOM IWAD file (for example
DOOM.WAD,DOOM1.WAD,DOOM2.WAD) for runtime testing
Note: IWAD files are not shipped with this repository.
Use build.py from this repository root.
python .\build.py `
--compiler "C:\path\to\MiniLangCompilerPy\mlc_win64.py" `
--std "C:\path\to\MiniLangCompilerPy\std"What this does:
- Compiles
tools/exe_icon_injector.mltobuild/tools/exe_icon_injector.exe - Compiles
tools/wad_upscale.mltobuild/tools/wad_upscale.exe - Compiles
src/i_main.mltobuild/MiniDoom.exe - Injects
icons/MiniDoom.icointobuild/MiniDoom.exe
Final output:
build/MiniDoom.exe
build/tools/wad_upscale.exe
--output-dir <path>: change output directory--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-upscale-tool: build withouttools/wad_upscale.ml
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 `
--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 `
--subsystem console
.\exe_icon_injector.exe .\MiniDoom.exe .\icons\MiniDoom.icoExample run:
.\build\MiniDoom.exe -iwad "C:\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" -openglOpenGL 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.
MiniDoom can load an optional sidecar graphics package next to the original WAD:
DOOM2.WAD
DOOM2.WAD.UPSCALED
Generate the package with palette-aware xBRZ scaling:
.\build\tools\wad_upscale.exe "C:\Games\DOOM\DOOM2.WAD" "C:\Games\DOOM\DOOM2.WAD.UPSCALED" 2Run with a physical presentation scale and the package:
.\build\MiniDoom.exe -iwad "C:\Games\DOOM\DOOM2.WAD" -renderscale 2 -upscaled "C:\Games\DOOM\DOOM2.WAD.UPSCALED"If -upscaled is omitted, MiniDoom automatically tries <iwad>.UPSCALED. Missing upscaled graphics fall back to the original WAD.
The upscaled package stores prepared wall textures, flats, sprites, HUD/menu patches, fonts, and full-screen patch graphics. During normal high-resolution gameplay, MiniDoom consumes those prepared images directly; the xBRZ-style scaler belongs to the offline wad_upscale tool rather than the frame loop.
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).
- 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>
- 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).
- Core engine behavior targets original DOOM parity while using MiniLang runtime semantics.
- Platform layer is adapted for modern Windows execution.
- Build and tooling are modernized (single Python build script + MiniLang resource tool).
See LICENSE.





















































































































