Brutal Doom in the browser, booting straight into the map

A fork of a UZDoom WebAssembly port that boots straight into one game when the page loads. Open the URL on a TV and you're in MAP01.

published

length2 min read

There's a WebAssembly port of UZDoom that runs in the browser, and it's really good. It has a picker where you upload an IWAD, add mods, and hit Launch. I wanted something simpler: open the browser on my TV and have Doom 2 with Brutal Doom already starting up.

So my fork is a container that you point at a folder of WADs, and it boots one game.

Boot config

The fork adds a boot.json file next to the WADs:

{ "iwad": "doom2.wad", "file": "brutal22test6.pk3", "warp": 1, "skill": 4 }

With that in place, going to the address loads Doom 2 with Brutal Doom and drops you right into MAP01 on Ultra-Violence, no menu. The first visit downloads the WADs with a progress bar. After that they're stored in the browser's IndexedDB, so the game starts right away. If you drop another file in the mounted folder, you can load it by name without registering it anywhere.

URL parameters override the file, so a shared link with ?warp=7 still works on a kiosk setup. Every parameter gets checked against a strict pattern before it gets anywhere near the engine, and anything that doesn't match gets thrown out.

The bug I fixed

Upstream ships with Brutal Doom's damage overlays turned off, because they were covering the whole screen in almost solid red. The cause turned out to be a HUD message limit that was applied per font. Brutal Doom draws damage using about twenty different graphics over the same pixels, so even with a limit of four per font, you could still end up with around eighty translucent layers stacked on top of each other. Applying one limit across all of them fixed it, and the overlays are back on.

Two settings that will break it

WebAssembly threads need SharedArrayBuffer, and browsers only allow that on pages that are cross-origin isolated and served from a secure context. If either one is missing, the engine's threads never start.

  • The bundled Caddy sets Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp. If you put your own reverse proxy in front of it, make sure both headers reach the browser unchanged.
  • It has to be served over HTTPS. A plain HTTP address on your LAN doesn't count as a secure context, so the engine won't start. Plain HTTP only works on localhost.

The fork also builds on Linux and macOS, while upstream's helper tools only came as Windows binaries. WADs aren't included, so you'll need to bring your own.