Skip to content
| Marketplace
Sign in
Visual Studio Code>Visualization>DOOM for VS CodeNew to Visual Studio Code? Get it now.
DOOM for VS Code

DOOM for VS Code

Peter Banfi

|
1 install
| (0) | Free
Play classic DOOM in VS Code (WebAssembly). Chocolate Doom + FreeDOOM, custom WAD support.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

DOOM for VS Code 🔫

Play classic DOOM directly inside Visual Studio Code using WebAssembly. Powered by Websockets Doom — a Chocolate Doom port compiled to WASM via Emscripten. Uses FreeDOOM game data (downloaded automatically on first run). Commercial WADs are also supported.

Search terms: doom, vscode doom, doom extension, chocolate doom, freedoom, wad, webassembly, retro fps.


DOOM for VS Code

Play!

Ctrl+Shift+P  →  DOOM: Play DOOM 🔫

FreeDOOM (~56 MB) is downloaded automatically on first run and cached locally. Subsequent launches are instant.


Controls

Key Action
W / ↑ Move forward
S / ↓ Move backward
A / ← Turn left
D / → Turn right
Alt + A / D Strafe
Shift Run
Ctrl Fire
Space / E Use / Open door
Tab Automap
Esc Menu
1–7 Select weapon
Mouse Look / aim (click the canvas to capture)

Settings

Open with Ctrl+Shift+P → DOOM: Configure WAD File, or edit your VS Code settings.json:

Setting Default Description
doom.wadPath "" Absolute path to a custom .wad file. Leave empty to use auto-downloaded FreeDOOM.
doom.enableSound true Enable sound effects. Disable if you experience audio issues. Note: music is always off (see Known Limitations).

Developer notes

Using a Commercial WAD

If you own Doom 1 or Doom 2 (available on Steam or GOG):

// settings.json
"doom.wadPath": "C:/path/to/your/doom.wad"

Any IWAD compatible with Chocolate Doom works: doom.wad, doom2.wad, tnt.wad, plutonia.wad.


Known Limitations

  • No music — MIDI / OPL music initialisation can hang in a browser WebAudio context in this build. Music is disabled by default (-nomusic flag). Sound effects work fine.
  • No save games — Emscripten's virtual filesystem is in-memory. Saves are lost when the panel is closed.
  • Mouse capture — Click the canvas to capture the mouse for mouselook. Press Esc to release. VS Code may show a "pointer captured" indicator in the status bar.
  • One panel at a time — Running the command when a DOOM panel is already open just brings it to focus instead of starting a second instance.

Architecture

doom-extension/
├── src/
│   ├── extension.ts      — activate(), registers doom.play + doom.configureWad commands
│   ├── DoomPanel.ts      — WebviewPanel singleton; generates the HTML page with canvas
│   └── wadManager.ts     — Downloads FreeDOOM WAD to globalStorageUri on first run
├── media/
│   ├── websockets-doom.js    — Emscripten JS glue (Chocolate Doom, not included)
│   └── websockets-doom.wasm  — Compiled Chocolate Doom WASM binary (not included)
└── scripts/
    └── fetch-doom-assets.js  — Helper script to download engine assets automatically

How it works

  1. doom.play command fires → wadManager.ensureWad() checks / downloads FreeDOOM
  2. DoomPanel.createOrShow() opens a WebviewPanel with a canvas and a start-screen overlay
  3. User clicks ▶ PLAY DOOM → AudioContext is created (browser autoplay policy requires a user gesture)
  4. window.Module is configured with Emscripten options, then websockets-doom.js is injected via <script>
  5. WASM compiles, FreeDOOM WAD is fetched into Emscripten's virtual filesystem
  6. onRuntimeInitialized fires → global callMain(['-iwad', 'freedoom1.wad', '-window', '-nogui', '-nomusic', ...]) starts the engine
  7. Chocolate Doom renders into the <canvas> via SDL2 → WebGL; audio via Web Audio API

Key VS Code WebView constraints solved

Challenge Solution
No SharedArrayBuffer in webview Websockets Doom uses a single-threaded Emscripten build — no COOP/COEP headers needed
Emscripten uses eval() CSP includes 'unsafe-eval' in script-src
WASM file path wrong in sandboxed iframe Module.locateFile() remaps .wasm → webview.asWebviewUri()
56 MB WAD would bloat the VSIX Downloaded to context.globalStorageUri on first run
AudioContext autoplay blocked AudioContext.resume() called inside the click handler of the Play button
callMain is a global, not on Module Call bare callMain(args) — this build never assigns it to Module
Canvas context poisoning Never call canvas.getContext('2d') — SDL claims the canvas for WebGL; requesting 2D first locks it and prevents WebGL from initialising
Music hanging engine Always pass -nomusic; OPL/MIDI init stalls in browser WebAudio

License

Extension code: MIT
FreeDOOM: FreeDOOM License (freely redistributable)
Chocolate Doom / Websockets Doom: GPL-2.0

This extension does not bundle the DOOM engine or any WAD files. Engine assets (websockets-doom.js / .wasm) must be provided separately. FreeDOOM game data is downloaded from the official FreeDOOM GitHub releases.


  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft