Skip to content

About

Doom recreated natively in Dart, Flutter and Flame 3D, with Wasm/WebGPU browser rendering.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Doompeller

Doompeller is a native Dart + Flutter + Flame 3D reimplementation with a pure Dart WAD parser, geometry compiler, and deterministic 35 Hz gameplay core. It does not embed, wrap, download, or extract another Doom engine.

See runtime architecture for loading, state ownership, rendering and audio boundaries.

Production: doompeller.castletaste.dev. CI and PR previews: publishing guide.

Run original E1M1

Use Flutter 3.47.7 and fetch the pinned shareware E1 content before resolving the app's assets, or provide the same verified DOOM1.WAD yourself:

python3 tool/fetch_shareware.py
flutter pub get --enforce-lockfile
flutter pub run flame_3d:build_shaders

The file stays ignored at .local/doom/DOOM1.WAD; it is bundled into the app and public site. The app starts E1M1 without a chooser. DOOM_WAD_PATH can override the desktop source when the process has permission to read the file.

The intermission continues through episode 1 with health, armor, ammo and weapons retained. The E1M3 secret exit leads to E1M9, which returns to E1M4; E1M8 is the episode finale. See verification for the distinction between implemented mechanics, scene checks and completed replays.

flutter run -d macos --release

Leave DOOM_WAD_PATH unset for the normal macOS release: it uses the bundled local original WAD. The macOS app sandbox may reject an external path override, including a relative path into the source checkout. An explicit override is never silently replaced with the bundled copy if reading it fails.

If a configured path is unreadable, invalid, not an IWAD, or lacks E1M1, the app shows an error. It will not silently switch content; the synthetic fallback requires an explicit button press.

Run in a WebGPU browser

The HTML startup screen shows loading and offers retry on errors or slow loading. Graphics and IWAD loading failures also have explicit retry actions. Browser focus loss pauses the game; returning to the tab leaves it paused. Controls hints hide on player movement and return after six idle seconds.

The browser target is Flutter Wasm plus flame_3d's WebGPU backend. The release build packages .local/doom/DOOM1.WAD at build/web/assets/.local/doom/DOOM1.WAD, and startup loads E1M1 automatically. SELECT LOCAL IWAD remains available only from the pause menu for switching content at runtime.

Browser audio uses the WAD's original PCM effects and MUS/GENMIDI music. A pure-Dart OPL2 synthesizer runs in a separate Wasm worker, with an AudioWorklet feeding the browser output. Click or press a key to unlock sound. The pause menu has independent music and effects volumes, retained until the app closes. Pause and browser blur freeze music; restarting a level restarts its track. If the browser cannot provide the music transport, the pause menu reports the failure while effects remain available.

Build the reproducible release with Flutter 3.47.7, naga-cli 30.0.1 and ripgrep (rg) on PATH:

DOOMPELLER_FLUTTER="$(command -v flutter)" bash tool/build_web_release.sh

The build removes Flutter's generated dart2js fallback and verifies the dart2wasm entrypoint, music worker/worklet, local CanvasKit assets, generated WGSL shader bundle, deployment headers, and the exact bundled DOOM1.WAD size and checksum. Deployment must preserve web/_headers so Wasm/worker resources run under COOP/COEP. The supported and tested runtime target is Wasm/WebGPU.

Headless WAD report

Before opening the app, inspect a WAD and compile its map geometry without a GUI or a macOS build. The command reads the selected WAD into memory only; it does not extract or write any Doom content. With no path it reports the clean unit-test fixture; CI acceptance uses the original shareware WAD.

dart run tool/wad_report.dart \
  --map E1M1 .local/doom/DOOM1.WAD

# Or use DOOM_WAD_PATH; --json is suitable for CI artifact parsing.
DOOM_WAD_PATH=.local/doom/DOOM1.WAD \
  dart run tool/wad_report.dart \
  --json

The report includes container and map counts, resolved and missing texture/ flat names, geometry gaps and fallbacks, atlas/surface vertex budgets, and parse/compile timings. READY, READY WITH FALLBACKS, and PROBLEMS are the final verdicts; PROBLEMS exits non-zero.

Controls

  • W/S or up/down: move forward/back
  • A/D: strafe
  • hold either Shift: run
  • left/right: turn
  • Enter, numpad Enter, Ctrl or primary click: attack
  • Space or E: use
  • 1–6: fist, pistol, shotgun, chaingun, rocket launcher, chainsaw
  • Browser: click the game to capture the mouse, move to turn, primary click to fire
  • Browser: Esc releases the cursor and pauses; click RESUME to continue
  • Browser: wheel or trackpad vertical scroll cycles owned weapons, including over the HUD
  • Native: Esc pauses/resumes; primary-button drag turns
  • The browser pause menu includes session-only mouse sensitivity

All simulation input is sampled into TicCmd. Rendering interpolates the camera between completed 35 Hz tics and never advances game state.

Browsers with a coarse primary pointer start with smartphone controls. Touching a hybrid device also enables them; an explicit choice in the pause menu wins. The menu has a TOUCH CONTROLS toggle. Drag the left stick to move/strafe, swipe the right side to turn, and hold FIRE to shoot (dragging it also turns). Hold RUN for the existing run speed. USE, weapon selection, map/zoom, and pause have dedicated buttons. Controls fit portrait and landscape layouts and release held input when paused, cancelled, or the app loses focus.

Verify

Use the pinned Flutter 3.47.7 toolchain:

flutter analyze
dart run tool/test.dart
flutter build macos --release

The default app test runner needs no IWAD. It runs in the checkout with temporary empty placeholders for the local-content asset and generated shader directory; they are removed after the run and the production manifest is unchanged. Resolve dependencies with flutter pub get first. Additional arguments are passed to flutter test.

Original-content acceptance is explicit and reads your local file directly:

DOOM_WAD_PATH=/absolute/path/to/DOOM1.WAD \
  /Users/savva/fvm/versions/stable/bin/dart run tool/test.dart --include-content

This includes collision challenges, effect lifetimes, episode progression and deterministic traversal. The content tag distinguishes these tests from the synthetic suite. Neither test mode copies the WAD into its temporary project. Run dart analyze --fatal-infos and dart test in each of packages/doom_wad, packages/doom_geometry, packages/doom_core, and packages/doom_music for the pure-Dart suites. node --test test/web/doom_music_worklet_test.mjs verifies the bounded output protocol; tool/web_audio_harness/build.sh builds the local browser transport stress harness. The macOS build still requires the local WAD described above.

About

Doom recreated natively in Dart, Flutter and Flame 3D, with Wasm/WebGPU browser rendering.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages