doom-on-github

Doom on GitHub

Doom on GitHub

Play Doom on your GitHub contribution graph — a Chrome extension that compiles DoomGeneric to WebAssembly and renders every frame live onto the real contribution grid.

Manifest V3 C / WebAssembly Chrome status: active

Install · How it works · Build from source · ABI spec · Privacy


Install

Get it from the Chrome Web Store.

Then on any GitHub profile: open the contribution graph’s Contribution settings menu (top right of the graph) and check Doom. The graph expands to a taller grid and a live game starts immediately — no setup, no save file, nothing to configure.

Prefer to run it from source? See Build from source.

How it works

Architecture

Piece What it does
doomgeneric_github.c The DoomGeneric backend: owns the framebuffer, downsamples Doom’s 320×200 render to the graph’s resolution, and exposes a tiny C ABI (see docs/SPECS.md) for the JS host to drive.
extension/src/doom/worker.js Runs the compiled WASM module in a dedicated Web Worker at Doom’s native 35Hz tick rate, off the page’s main thread.
extension/src/doom/frame.js + engine.html A hidden iframe hosting the worker, relaying input in and frames out via postMessage.
extension/src/doom/display-driver.js Expands the real contribution grid to the taller display size and paints each cell by setting its data-level attribute — the exact mechanism GitHub’s own CSS uses to color a real contribution square.
extension/src/content.js + utils.js Finds the contribution graph’s settings menu and injects the Doom toggle into it.

Nothing is downloaded at runtime: the WASM binary, the shareware IWAD, and the Doom engine are all bundled in the extension. No network requests, no accounts, no data collected — see PRIVACY.md.

Build from source

Requires the Emscripten SDK on PATH.

emsdk_env    # or emsdk_env.bat / emsdk_env.ps1, from your emsdk checkout
make -f Makefile.emscripten clean
make -f Makefile.emscripten

This produces engine.js / engine.wasm at the repo root — copy both into extension/src/doom/ before loading the extension.

Load the extension locally: chrome://extensions → enable Developer mode → Load unpacked → select the extension/ folder.

Test the engine without the extension: serve the repo root (python -m http.server 8080) and open test/pipeline.html, which drives the exact same worker/iframe/input pipeline the real extension uses.

ABI

The C ↔ JS boundary is small and documented in full in docs/SPECS.md: a handful of exported functions (DG_Github_Init, DG_Github_Input, DG_GetFramebufferPtr, doomgeneric_Tick) and one flat framebuffer, one byte per cell, values 0–4 matching GitHub’s contribution levels.

Acknowledgments