Destino runs Doom in a terminal using Node.js, node:ffi, and OpenTUI.
The Doom engine is built from doomgeneric as a native shared library. Node.js owns the application loop, forwards terminal input to Doom, reads Doom's framebuffer through FFI, and asks OpenTUI to render it as terminal graphics.
Sound is delegated to DoomGeneric's SDL2 audio backend through SDL2_mixer; Node.js only coordinates the process and does not synthesize or mix audio itself.
The project is split into a small native platform layer and a JavaScript runtime:
src/native/main.cimplements the platform callbacks required bydoomgeneric, including input, timing, frame readiness, and framebuffer access.src/engine.jsloads the Doom shared library throughnode:ffiand exposes a small JavaScript wrapper around the native functions.src/input.jsparses terminal keyboard input, including Kitty keyboard protocol events, and maps configured keys to Doom key codes.src/opentui.jsloads OpenTUI's native library and renders Doom's framebuffer into the terminal.src/index.jswires everything together and runs Doom at 35 Hz.
Rendering is pull-based: Doom marks a frame ready, JavaScript pulls the native framebuffer, scales it into a reusable buffer, and passes that buffer to OpenTUI. This avoids C-to-JS callbacks in the frame path.
You need:
- A Node.js build with
node:ffisupport. cmake,clang,pkg-configandunzip.- SDL2_mixer development files.
doomgenericsources underdeps/doomgeneric.- A Doom-compatible WAD, such as
freedoom1.wadfrom Freedoom. - An SF2 sound font, such as GeneralUser GS.
- A terminal with Kitty keyboard protocol support, used for reliable key press and release events.
This is only supported on macOS or Ubuntu Linux.
npm install
npm run dependencies
Install NPM dependencies:
npm install
On macOS, install native dependencies with:
brew install clang pkg-config sdl2_mixerOn Linux install SDL2, SDL2_Mixer, clang, pkg-config and unzip according to your distribution package manager.
Download runtime assets and doomgeneric locally:
mkdir -p deps
cd deps
curl -sSL -o doomgeneric.zip https://github.com/ozkl/doomgeneric/archive/refs/heads/master.zip
curl -sSL -o freedoom.zip https://github.com/freedoom/freedoom/releases/download/v0.13.0/freedoom-0.13.0.zip
curl -sSL -o GeneralUser.sf2 https://github.com/mrbumpy409/GeneralUser-GS/raw/refs/heads/main/GeneralUser-GS.sf2
unzip doomgeneric.zip
mv doomgeneric-master doomgeneric
unzip freedoom.zip
mv freedoom-0.13.0 freedoom
rm *.zip
cd ..Build the native Doom library:
npm run buildDestino can be packaged as a Node.js Single Executable Application (SEA) on macOS.
First install dependencies and build the native library:
npm install
npm run dependencies
npm run buildThen build the executable:
npm run seaThis bundles the JavaScript entry point, native libraries, WAD files, and SF2 sound font into dist/destino.
The SEA config also enables --experimental-ffi automatically, so the executable can be run directly:
./dist/destinoDestino reads configuration from destino.json in the current directory by default. If the file does not exist, it creates one and exits.
Run once to generate the config:
/path/to/node --experimental-ffi src/index.jsUpdate wadPath and sf2Path if they were not detected automatically, then run again:
/path/to/node --experimental-ffi src/index.jsYou can also pass a custom config path as the first argument:
/path/to/node --experimental-ffi src/index.js ./foo.jsonControls are configured in destino.json. Press Ctrl+C to exit.
Default keybindings:
| Action | Keys |
|---|---|
| Move forward | w, up |
| Move backward | s, down |
| Turn left | a, left |
| Turn right | d, right |
| Strafe left | q, , |
| Strafe right | e, . |
| Fire | space, ctrl |
| Use | enter |
| Menu | escape |
| Pause | p |
| Confirm | enter |
| Abort | escape |
| Quit confirm | y |
| Quit abort | n |
Destino is distributed as GPL-3.0-or-later because the Doom binding links with the GPL-licensed Doom engine sources. See LICENSE.
Files that do not directly bind to, build, or embed the Doom engine are licensed under the MIT License and carry an SPDX-License-Identifier: MIT header. See LICENSE-MIT.
The files that directly bind to or build the Doom engine carry an SPDX-License-Identifier: GPL-3.0-or-later header.