quake-rb
A port of the Quake (1996) engine to Ruby. Reads original .pak/.bsp/.mdl assets and renders them via OpenGL + SDL2.

Why
Mostly as a counterpart to doom — to see how far you can take id Software's classic engines in plain Ruby, and to learn the BSP/PVS/lightmap pipeline by writing it from the spec rather than reading the C.
Quick start
bundle install
bundle exec bin/quake
On first run, with no data/id1/pak0.pak present, you'll be asked to download the freely-redistributable shareware Quake (Episode 1, ~17 MB). It's pulled from archive.org/quakeshareware/QUAKE_SW.zip and extracted into ./data/id1/pak0.pak. You can also drop your own registered pak0.pak (and pak1.pak) in there manually.
System requirements:
- Ruby 3.1+
- SDL2 (
brew install sdl2/apt install libsdl2-dev) - OpenGL 2.1
unzip(used by the shareware fetcher)
Controls
| Key | Action |
|---|---|
| WASD | Move |
| Mouse | Look |
| Space | Jump |
| C | Crouch |
| 1–8 / scroll | Weapon select |
| N | Toggle noclip |
| L | Dump player pose + save a screenshot to debug/shots/ (debug aid) |
| Esc | Quit |
What works
- BSP loading + face extraction with texture coordinates
- Texture mapping with the original 256-colour palette
- Static lightmaps from the lighting lump
- PVS culling, including:
- leaf 0 fallback (treat solid leaf as all-visible, à la TyrQuake) - FatPVS expansion when the camera leaf touches a liquid surface - **vis-through-water**: liquid faces in PVS bring the leaves on the other side of their plane into view, so translucent water shows the cave bed instead of clear-color void - Sky rendering: two-layer scrolling cloud + back layer, PVS-culled
- Water/slime/lava: turbsin-warped texture, alpha-blended translucency, backface culling for the duplicate front/back face pairs Quake's BSP compiler emits
- Brush entities (doors, plats, buttons, triggers) with their own transforms
- MDL alias models with frame interpolation
- Particle effects + viewmodel + HUD
- Player physics: walk, crouch, jump, friction, water levels, platform riding
- Item pickups, weapon switching, Quake-style player weapon firing, shareware audio mixer
- Common QuakeC trigger behavior at engine level, including target firing, killtarget, counters, hurt/push/teleport triggers, and changelevel requests
What doesn't (yet)
- Multiplayer / netcode
- QuakeC server program — game logic is faked at engine level
- Full savegame/spawn parameter preservation across episode/level transitions
- Mirrors, particle warp, dynamic lights
- Sky cube / skybox replacement
- Demo playback
Maps
The shareware PAK ships:
start, e1m1, e1m2, e1m3, e1m4, e1m5, e1m6, e1m7, e1m8
Pass -map maps/e1m3.bsp to load a different one.
Architecture
lib/quake/
bsp/ reader, types, face vertices, PVS / FatPVS / vis-through-water
pak/ pak0.pak reader
mdl/ alias model reader
wad/ gfx.wad (HUD/menu graphics) reader
palette.rb 256-colour palette → RGBA
math/vec3.rb plain Ruby vector
physics/ hull trace + Player (walk/swim/jump/noclip)
game/
engine.rb orchestrates renderers + physics + entity simulation
brush_entities entity-driven door/plat/button state machines
item_pickups touchable items
player_state weapons, ammo, health, armor
renderer/
gl_textured opaque world brushes (with lightmap pass)
gl_brush_model sub-model brushes (doors etc.) at entity transforms
gl_alias_model .mdl monsters + items
gl_water *water0/*slime0/*lava0/*teleport with turbsin warp
gl_sky two-layer scrolling sky
gl_lightmap lightmap atlas builder
gl_texture_manager mipmapped diffuse texture cache
gl_particles explosion/spark sprites
gl_viewmodel first-person weapon
gl_hud statusbar + numbers
sound/ SDL_mixer-backed sound effects
pak_downloader.rb shareware fetcher
version.rb VERSION = "0.1.0"
bin/
quake interactive game
quake-debug headless DSL runner (`teleport`, `screenshot`, `dump` etc.)
test/ minitest suite
debug/scripts/ ad-hoc investigation scripts (gitignored)
The headless runner is genuinely useful for working on the engine without a window — see debug/scripts/* for examples (water visibility traces, leaf inspection, BSP dumps).
Development
# Run the tests
bundle exec ruby -Itest -Ilib -e 'Dir["test/**/*_test.rb"].each { |f| require File.expand_path(f) }'
# Run a headless debug script (renders to a hidden window)
bundle exec bin/quake-debug debug/scripts/water_real.rb
# Run the game with YJIT
bundle exec bin/quake --yjit
Status
This is a hobby project. It can comfortably walk you through e1m1, fire Quake-style player weapons, pick up items, and use a growing subset of QuakeC brush/trigger behavior. The QuakeC server program is still not implemented, so game logic is ported incrementally at engine level and monsters don't fight back yet.
License
GPL-2.0-only, matching the original Quake source release.
The shareware Quake data is © 1996 id Software, freely redistributable. The registered Quake data (if you supply your own pak0.pak + pak1.pak) is not redistributable; you must own a copy.