DOOM Ruby
A faithful port of the DOOM (1993) engine to Ruby. Play original WAD files with the classic software renderer, a hardware rasterizer, or a GPU ray tracer, in single-player or multiplayer.

Features
- Three renderers: Classic BSP software rendering, OpenGL rasterization, and GPU ray tracing
- Dynamic lighting: Projectile, explosion, emissive-floor, flashlight, shadow, and fog effects in the ray tracer
- Multiplayer: Authoritative server/client play and deterministic peer-to-peer lockstep, with co-op and deathmatch
- Classic rendering: BSP traversal, visplanes, drawsegs, sprite clipping, and sky rendering matching Chocolate Doom
- Combat: Hitscan weapons (pistol, shotgun, chaingun), melee (fist, chainsaw), projectile rockets with splash damage
- Items: Weapons, ammo, health, armor, keys -- all pickupable with correct DOOM behavior
- Movement: Momentum-based physics with friction, smooth step transitions, wall sliding, view bob
- HUD: Full status bar with ammo, health, armor, face, weapon selector, key cards, small ammo counts
- Effects: Animated textures (NUKAGE, SLADRIP), sector light effects (flickering, glowing, strobing), scrolling walls
- Monsters: Death animations, solid collision, HP tracking
- Compatibility: Supports original WAD files (shareware and registered), YJIT-optimized
Installation
gem install doom
Quick Start
Just run doom -- it will offer to download the free shareware version:
doom
Or specify your own WAD file:
doom /path/to/doom.wad
Controls
| Key | Action |
|---|---|
| W / Up Arrow | Move forward |
| S / Down Arrow | Move backward |
| A | Strafe left |
| D | Strafe right |
| Left Arrow | Turn left |
| Right Arrow | Turn right |
| Mouse | Look around (click to capture) |
| Left Click / X / Shift | Fire weapon |
| Space / E | Use (open doors) |
| 1-7 | Switch weapons |
| M | Toggle automap |
| R | Cycle classic, rasterizer, and ray-tracing renderers |
| Z | Toggle debug overlay |
| Escape | Release mouse / Quit |
Renderers
Select a renderer when starting the game:
doom --renderer=classic /path/to/doom.wad
doom --renderer=rasterizer /path/to/doom.wad
doom --renderer=raytracing /path/to/doom.wad
Press R during play to switch between them without restarting the simulation.
classicis the pixel-accurate software BSP renderer.rasterizerbuilds a textured 3D world and draws it through Gosu's OpenGL context with a depth buffer.raytracingtraces primary, shadow, reflection, refraction, and indirect-light rays on the GPU using a stackless BVH. It includes dynamic lights, hard shadows, emissive nukage, fog, and a flashlight. It does not require dedicated hardware ray-intersection units.
The ray tracer uses a hybrid raster pass for sprites. Its secondary rays are a single-bounce approximation rather than a converged path-traced image. Fog, the flashlight, and secondary rays can be toggled in the Options menu. See Modern renderers for implementation details and current limitations.
Multiplayer
For an authoritative session suitable for more players over the internet, run a headless server and connect clients to it:
doom --serve --players=4 --port=5029 /path/to/doom.wad
doom --join=server.example.com:5029 /path/to/doom.wad
For a small LAN, the original-style deterministic lockstep mode is also available:
doom --host --players=2 --port=5029 /path/to/doom.wad
doom --connect=192.168.1.10:5029 /path/to/doom.wad
Add --deathmatch to host or serve a deathmatch. --frags=N sets its frag
limit. Every participant must use the same WAD.
Frame rate
Frame generation is decoupled from presentation: the engine renders as fast as
it can and only presents to the display at its refresh rate, so the reported
FPS is not clamped to vsync. The debug overlay (Z) shows both numbers --
frames generated, and frames actually shown alongside the display's refresh
rate.
Turn it off with UNCAPPED FPS in the options menu, or start with --vsync
to present every frame the old way.
Requirements
- Ruby 3.1+ (Ruby 4.0 with YJIT recommended for best performance)
- Gosu gem (for window, sound, and graphics)
- SDL2 (native library required by Gosu)
- OpenGL 3.3 or newer for the rasterizer and ray tracer
Installing SDL2
macOS:
brew install sdl2
Ubuntu/Debian:
sudo apt-get install build-essential libsdl2-dev libgl1-mesa-dev libopenal-dev libsndfile1-dev libmpg123-dev libfontconfig1-dev
Fedora:
sudo dnf install SDL2-devel mesa-libGL-devel fontconfig-devel gcc-c++
Arch Linux:
sudo pacman -S sdl2 mesa
Windows: No additional setup needed -- the gem includes SDL2.
Development
git clone https://github.com/khasinski/doom.git
cd doom
bundle install
ruby bin/doom
Run specs:
bundle exec rspec
Benchmarking
ruby bench/benchmark.rb # without YJIT
ruby --yjit bench/benchmark.rb # with YJIT
ruby bench/benchmark.rb --compare # side-by-side
ruby bench/benchmark.rb --profile # CPU profile with StackProf
Technical Details
- BSP Traversal: Front-to-back rendering using the map's BSP tree with R_CheckBBox culling
- Visplanes: Floor/ceiling rendering with R_CheckPlane splitting and span-based drawing
- Drawsegs: Wall segment tracking for proper sprite clipping (silhouette system)
- Texture Mapping: Perspective-correct ray-seg intersection with non-power-of-2 support
- Lighting: Distance-based light diminishing with wall and flat colormaps
- Sky Rendering: Chocolate Doom sky hack (worldtop = worldhigh) with correct placement
- Movement Physics: Continuous-time momentum/friction matching Chocolate Doom's P_XYMovement
- Hitscan: Ray tracing against walls and monster bounding circles
- Projectiles: Physical rockets with wall/monster collision and splash damage
Performance
With Ruby 4.0 and YJIT enabled, the renderer achieves 80-130 FPS on E1M1 (Apple Silicon). See docs/performance-profiling.md and docs/yjit-vs-zjit.md for detailed analysis.
Legal
DOOM is a registered trademark of id Software LLC. This is an unofficial fan project.
The shareware version of DOOM (Episode 1) is freely distributable. For the full game, please purchase DOOM from Steam, GOG, or other retailers.
License
GPL-2.0-only -- Same license as the original DOOM source code.
Author
Chris Hasinski (@khasinski)