https://github.com/cedrickchee/doom-wasm
Porting DOOM to WebAssembly from scratch, without much magic tooling or frameworks (i.e, Emscripten) on our way.
Science Score: 13.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
○.zenodo.json file
-
○DOI references
-
○Academic publication links
-
○Academic email domains
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (11.5%) to scientific vocabulary
Keywords
Repository
Porting DOOM to WebAssembly from scratch, without much magic tooling or frameworks (i.e, Emscripten) on our way.
Basic Info
Statistics
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
- Releases: 0
Topics
Metadata Files
README.md
Porting Linux DOOM to WebAssembly
We got vanilla Linux DOOM starting from Rust.

libc
Getting musl
AR=llvm-ar-10 CC=clang CFLAGS="-m32 --target=wasm32" ./configure --target=wasm32
no wasm32 arch support
Getting the arch from https://github.com/emscripten-core/emscripten/tree/efede793113ce1aa4d38d4f2df08e6b251cc53c6/system/lib/libc/musl/arch/emscripten
Throwing out everything which is complicated.
Crossover of musl 1.2.2 and arch from emscripten of musl 1.1.15.
Only need the string formatting functions anyway. Kicking out everything else.
Only making make lib/libc.a.
compiler rt for builtins
From: https://compiler-rt.llvm.org/
For example, when compiling for a 32-bit target, converting a double to a 64-bit unsigned integer is compiling into a runtime call to the "__fixunsdfdi" function.
https://00f.net/2019/04/07/compiling-to-webassembly-with-llvm-and-clang/
provides a precompiled libclang_rt.builtins-wasm32.a, which brings down the
missing imports to 51. The result looks very promising. But I want to build a
minimal version myself.
Get https://github.com/llvm/llvm-project/
llvm-project/compiler-rt/lib/builtins sources and compile myself. No need for
arch I hope, no assembly to be emitted. But using git tag llvmorg-11.1.0.
Dat feel! After so much theory and no way to test. Finally seeing the first screen of Doom rendered. Awesome!

Doom rendering broken colors, but can read text:

Start screen rendering correctly on an HTML5 canvas:
We mapped Doom's X11 ColorMap to canvas's RGBA color:

If I don't make I_FinishUpdate panic!(), then Doom runs in its infinite game
loop. Unfortunately, this runs at 100% CPU, Firefox complains that a website is
misbehaving, and nothing is rendered, since the browser has no chance of drawing
the animation.
Probably, I want to change Doom such that doom itself is not looping, but I can
call the loop via window.requestAnimationFrame().
This somehow inverses control and gives the browser a chance to render the frames.
Project Structure
A summary of the directory structure:
- build.rs: Rust build script. Tells the rust compiler to build and link to
our small libc, compiler runtime, and doom library.
- clang_compiler_rt: C compiler runtime, to compile as static archive.
- musl-1.2.2: libc for C string functions, to compile as static archive.
- linuxdoom-1.10: original doom sources, to compile as static archive.
- doom1.wad: Doom game file.
- src: Rust sources.
- index.html: HTML and Javascript to load the compiled WebAssembly and provide
keyboard input and HTML5 canvas rendering output.
Optimizations
There is more to optimize for web-native performance.
The firefox performance profiler says we spend most of our time in
gettimeofday. In
b1eab74,
we remove this implementation completely, avoiding the need to construct a
Date object in javascript and avoiding sedond and microsecond translation,
since Doom just cares about the milliseconds since the start of the game, which
happens to be what javascript's performance.now() provides.
The game runs at ~35 FPS on my machine, but Chrome performance debugging tools still show many dropped frames, since the browser wants to animate at 60 FPS. In addition, since Doom is still polling the time to know when it can proceed, this is super energy inefficient and gives the browser no room for background tasks, such as garbage collection. In a048af0, we make Doom to return immediately when running one step of its game loop if there is nothing to do, giving control back to the browser. Now, Doom still runs at ~35 FPS (this is what Doom was designed for), but the browser gets a chance to render its 60 animation frames per second and the system is mostly idle otherwise. I can clearly hear the difference, since my CPU fan is no longer spinning up when starting Doom.
Canvas
Copy video bufer one time less.
Before:
javascript
var doom_screen = new Uint8Array(memory.buffer, ptr, doom_screen_width*doom_screen_height*4);
var ctx = canvas.getContext('2d');
var render_screen = ctx.createImageData(doom_screen_width, doom_screen_height);
After:
javascript
var doom_screen = new Uint8ClampedArray(memory.buffer, ptr, doom_screen_width*doom_screen_height*4);
var render_screen = new ImageData(doom_screen, doom_screen_width, doom_screen_height);
var ctx = canvas.getContext('2d');
Chrome and Firefox Dev tools confirm that this makes the game run at least twice as efficient.
WebAssembly Specific Optimizations
I want to enable full optimization of the whole wasm binary.
I would love to use LLVM-supported cross-language C/Rust LTO here, but it looks
like wasm-ld-10 does not support this.
The -plugin-opt option is not supported by my wasm-ld-10 (and it looks like
neither v11 or v12 support it).
As reference, ld.lld would support this option. IIUC, LLVM currently does not
support LTO for wasm?
Makefile
wasm-opt -O3 -o doom.wasm ${BUILDDIR}/xdoom.wasm
Now go to https://cedrickchee.github.io/wasm-doom and start shooting monsters!
Owner
- Name: Cedric Chee
- Login: cedrickchee
- Kind: user
- Location: PID 1
- Company: InvictusByte
- Website: https://cedricchee.com
- Twitter: cedric_chee
- Repositories: 227
- Profile: https://github.com/cedrickchee
Lead Software Engineer | LLMs | full stack Go/JS dev, backend | product dev @ startups | 🧑🎓 CompSci | alumni: fast.ai, Antler.co
GitHub Events
Total
Last Year
Issues and Pull Requests
Last synced: 12 months ago
All Time
- Total issues: 0
- Total pull requests: 3
- Average time to close issues: N/A
- Average time to close pull requests: 7 minutes
- Total issue authors: 0
- Total pull request authors: 1
- Average comments per issue: 0
- Average comments per pull request: 0.0
- Merged pull requests: 3
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 0
- Pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Issue authors: 0
- Pull request authors: 0
- Average comments per issue: 0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
Pull Request Authors
- cedrickchee (3)
Top Labels
Issue Labels
Pull Request Labels
Dependencies
- lazy_static 1.4.0