Skip to content

Repository files navigation

webdoom

A slim, modern DOOM port for the browser, built directly from the id-Software/DOOM linuxdoom-1.10 source. 293 KB of wasm, zero client install, zero-config multiplayer.

  • Runs in stock Chrome and Firefox (WASM + WebGL2 + WebAudio), and in Edge, which is Chromium and is therefore not separately tested — see docs/promises-index.md rme-002
  • Uncapped framerate with 35 Hz-exact game logic (Crispy-style interpolation; "vanilla mode" toggle under OPTIONS on the launcher menu)
  • Modern controls: pointer-lock mouse, WASD, rebindable keys, analog twin-stick gamepad — Doom 1+2 re-release defaults
  • Authentic audio: DMX PCM sfx via WebAudio, music through an emulated OPL2 (Nuked OPL3) playing the IWAD's own GENMIDI bank
  • 1–4 players: instant single player; arcade lobby for network play (join order = color: Green/Indigo/Brown/Red — nothing to type)
  • PSX DOOM fire background on the launcher — chunky indexed-cell fire on a 64×40 grid, palette-matched, flares on every menu transition; measured < 1 ms/tick on the weakest network host
  • Deterministic-lockstep netcode over a server tic relay (see docs/netcode.md); verified by a headless harness that compares per-tic gamestate hashes across real clients
  • Server carries the WAD library (Ultimate Doom, Doom II, Final Doom, SIGIL, Master Levels, NRFTL, Chex Quest); clients cache by content hash via a service worker — the second load is served from that cache, and single player works offline

webdoom running Ultimate Doom E1M1 in Chrome

Ultimate Doom E1M1, captured from a headless Chrome by browser-sp. The framebuffer is DOOM's own 320×200, scaled 4:3 with nearest-neighbour — a widescreen mode existed between 2026-07 and 2026-09 and was removed; see spec.md §"Widescreen view — REVERSED".

Contributing: CONTRIBUTING.md — the tic-identity rule is the one that will bite. Security model, and what is deliberately not defended: SECURITY.md.

Quick start

WAD_SRC=host:~/doom-wads tools/fetch-wads.sh   # pull your WAD library, build manifest
source tools/emsdk-env.sh    # pinned emcc on PATH
make -C engine               # → build/doom.js + doom.wasm
(cd server && npm i)
./start.sh                   # http://<host>:8666/

LAN players (or tailnet peers) just open the URL. First player into the Multiplayer panel is Green, second Indigo, then Brown, Red. Anyone picks the game/map/skill/mode; anyone hits START; 3-2-1, everyone's in.

webdoom.service is a ready systemd unit. It runs node server/serve.js out of this directory, so the client files it serves are whatever is on disk while the server process is whatever was running when it started — those drift. tools/deploy.sh closes the gap: it runs the quick tier, refuses while anyone is mid-game (GET /api/status reports counts only), restarts the unit, and re-probes. tools/deploy.sh --check reports drift and changes nothing.

Layout

Path What
engine/core/ linuxdoom-1.10, vendored pristine in commit 1, patched in reviewable commits
engine/web/ web platform layer: video/audio/input/net + MUS→OPL sequencer
client/ vanilla-JS shell: lobby, WebGL2 renderer, input, audio, service worker
server/ Node ≥ 20, single process, single port; only dep ws
tools/ emsdk pin, WAD fetch/identify, test suites, bench harness, native sanitizer target
docs/ 33 documents — the index lists every one. Start with the glossary if a term is doing work you cannot see. The ones most people want: netcode, renderer, playsim, formats, bare-metal, perf, state-machine, engine-archaeology

Tests

tools/run-tests.sh            # everything: 83 legs, one at a time
tools/run-tests.sh --jobs 6   # the same legs six at a time, identical verdicts
                              # 83 of 83 pass in 9 min 59 s on alder (i9-12900K,
                              # 24 threads), --perf --require-complete, nothing
                              # skipped, 2026-09-17.
                              #
                              # 6 came from a sweep over the 82 legs that
                              # existed that morning, same verdicts in all three
                              # arms: 3 -> 10 min 42 s, 6 -> 9 min 37 s,
                              # 10 -> 9 min 24 s.  3->6 saves 65 s; 6->10 saves
                              # 13 more for twice the browsers.  The floor is
                              # n64-demos, 434 s of it on its own.
                              # The runner prints its own time; your host is not alder.
tools/run-tests.sh --quick    # no WADs, no build, no browser — what CI runs
tools/run-tests.sh --list     # the leg registry

Each leg is isolated: one red does not hide the rest, and the run ends with a table naming every leg, its verdict and the count it reported about itself. A leg whose prerequisites are absent is a SKIP with its reason, counted in that table, and --require-complete turns any skip into a failure.

What CI covers. Game data is not distributable, so the GitHub runner has no IWADs and runs the --quick tier — lint, the doc-drift gate, the state-machine and precache checks, the gate census, and the three fuzz suites. Everything that needs a WAD, a built engine or a browser (the sim and render goldens, netplay, the ASan and cross-architecture legs, the 22 browser legs) runs locally and says so. The workflow prints the list it did not cover.

Three legs deserve a sentence the registry cannot give them:

  • sim goldens: all 13 built-in IWAD demos replay headless with per-tic gamestate fingerprints pinned against golden traces, cross-validated tic-for-tic against an instrumented Chocolate Doom (44,580 tics; tools/build-choco-reference.sh, then node tools/demo-test.mjs --cross <binary>).
  • render goldens: per-tic framebuffer hashes over the same 13 demos, so a renderer change that moves a pixel fails at the exact tic.
  • cross-architecture: the freestanding core replays the same goldens on 32-bit ARM under qemu and on an emulated Nintendo 64, bit-identical.

License

GPL-2.0-or-later (the id Software source re-license; Nuked OPL3 is GPL-2). Game data (WADs) is not distributed with this repository.

About

A slim, modern, clean-room DOOM port for the browser — vanilla-exact simulation, zero-config LAN multiplayer, and a fully documented archaeology of the engine’s magic data

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages