castle-quake1
Quake 1 port using Castle Game Engine (Free Pascal / Object Pascal)
- Active · last change 11-3-2026
- ★ 4 on GitHub
README
Castle Quake
A port of Quake 1 to the Castle Game Engine (Object Pascal / Free Pascal). It plays the original Quake 1 Demo / Shareware data and the bundled open-source LibreQuake assets, on Windows, Linux and in the browser.

- 🌐 Play in the browser (WebAssembly)
- 📦 Windows, Linux, macOS and Android releases (the Android APK is signed with a debug key: allow installing from unknown sources)
- 📖 Architecture and engine mapping
- 🗺️ Quake 1 comparison and roadmap
Everything is parsed from the PAK archives at run time: BSP levels become X3D scene graphs, Alias models become animated meshes, textures and sounds are served to the engine through custom URL protocols. The gameplay is a port of the original rules (player physics, all weapons, all monsters, level flow), and the real progs.dat can run the game instead ("QuakeC mode", for mods). Inspired by mariuz/castle-doom.
Screenshots
![]() |
![]() |
| Monsters with Quake's AI: sight, chase, attacks, pain and gibs | Infighting and sound propagation wake the rest |
![]() |
![]() |
| All eight weapons, here the lightning gun beam | Water, slime and lava ripple like R_Turbulent |
![]() |
![]() |
| The LibreQuake episode hub (bundled) | The original start.bsp hub with -game quake |
![]() |
![]() |
| "Dynamic PBR" world lighting with shadow maps, instead of the lightmaps | QuakeC mode: progs.dat runs the level |
![]() |
|
| The PAK demos play back (protocol 15) | Deathmatch over UDP with the scoreboard |
| Arcane Dimensions, made for QuakeSpasm, with its own QuakeC: fence textures (the vines) and thousands of entities | AD's first map with the fog and water alpha its QuakeC sets |
| Player names and shirt / pants colors in a network game | Map records: the intermission tells you about a new best |
| Split-screen coop for up to four players | Random maps built from a seed |
| Photo mode with depth of field | Horde mode: waves of monsters with a score |
Features
World
- BSP v29 levels, and the BSP2 / 2PSB formats of large community maps, from
.pakfiles turned into X3D scene graphs, with triangle collision and the Quake clipping hulls for movement. - Colored light from
.litfiles, fog from the worldspawnfogkey and skyboxes from itsskykey (FitzQuake / QuakeSpasm maps), also in demos and network games (svc_fog,svc_skybox). - The scrolling two layer sky or a six face skybox (
skyworldspawn key withgfx/env/*.tga), fullbright texture pixels, translucent liquids seen from both sides, lava splashes. - Two world lighting modes: Quake's own lightmaps, all lightstyle layers blended in a GLSL effect with the live lightstyle values and dynamic lights (muzzle flashes, explosions), or dynamic PBR lighting by the map's light entities with real-time shadow maps.
- Dual-layer scrolling sky, turbulent liquid surfaces, animated textures, particles (gunshots, blood, explosions, trails, teleports), underwater warp and palette tints.
- Only the potentially visible set is drawn: the world is split by BSP subtrees and the parts outside the camera leaf's PVS are hidden (seen through translucent water too), and so are the monsters, items, missiles, gibs and doors outside it; console
novis 1draws everything. - Interactive brush entities: doors (including silver and gold key doors), platforms, buttons, trains, secret doors, triggers, teleporters, level changes.
Gameplay
- A port of the NetQuake player physics: friction and acceleration, air control, stair stepping, swimming, water jumps, bunny hopping.
- All eight weapons from
weapons.qc: hitscan, nails, bouncing grenades, rockets with splash damage, the lightning beam. - All thirteen monsters and both bosses with QuakeC data: sight checks,
SV_movestepchasing, attacks on animation frames, pain, death, gibbing, infighting, sound propagation. - Level flow: the hub with skill and episode portals, runes and episode gates, keys, secrets, level parms, intermission with counting tallies, the episode texts, the all-four-runes text and the Shub-Niggurath ending with the end-of-game text.
- Random maps:
-randommap [seed](or Single Player > Random Map,?randommap=newin the browser) builds a level in memory, rooms and corridors compiled into a real BSP with collision hulls, colored lightmaps, monsters getting harder toward the exit, weapons, ammo and health; the exit leads to the next seed, so the levels never end. The same seed is the same map everywhere, so a network game only needs its name (-host random42). - Horde mode:
-horde(or Single Player > Horde Mode) turns any map, a random one too, into waves of monsters that teleport in out of sight, stronger every wave, with a score and the best score per map. - Photo mode: P pauses the game (or a demo), hides the HUD and flies the camera; change the field of view and add depth of field, grayscale, sepia, a vignette or ambient occlusion, then save a photo up to four times the window size.
- Speedrun timer and ghost racing: finish a level and your best run comes back as a translucent ghost to race, with splits at checkpoints and at the end (
-speedrun, Options or the console'sspeedrun/ghost). - Map browser: Main Menu > Map Browser lists 1750 community maps and mods from Quaddicted with their ratings, downloads one (checked by SHA-256) and plays it; installed maps can be removed again. A map made for a mod (Quoth, Arcane Dimensions, Copper...) downloads the mod with it and plays with the mod's QuakeC. The zips stay on disk and are read as files are needed (a 300 MB mod does not take 300 MB of memory), and playing another map unmounts the mods it does not need. Also from the console:
maps,maps_install <id>,maps_play <id>,maps_remove <id>. - Languages: English, German, French, Spanish, Italian and Romanian for the menu, the HUD, the captions and the console help, following the system language or picked in Options > Language (
-lang defor one run). A translation is a gettext.pofile indata/lang/. - Accessibility: captions for the sounds that matter (monsters waking and attacking, doors, lifts, secrets, pickups) with a hint of where they came from, a high contrast HUD and reduced damage and pickup flashes, in Options or the console (
captions,hud_contrast,reduced_flashes). - Save and load (F6 / F9, console
save/load, Main Menu > Save Game / Load Game with eight slots and a thumbnail of each,-load <slot>), including projectiles and gibs in flight. - LAN games: Multiplayer > Find LAN Games lists the games hosted on the local network (map, players, deathmatch or coop) and joins one with a press.
- Coop bots:
-host e1m1 -coop -bots 2(or Multiplayer > Coop with 2 bots) adds bots that follow you through the level and fight its monsters. - Deathmatch rules:
-fraglimit N,-timelimit Mand-maps e1m1,e1m2,...end a level at a limit and play the maps in turn. - Scoreboard: Tab shows the players and bots by frags (and your ping) in a network game; a deathmatch intermission shows it too.
- Chat: in a network game T opens a line to say something to everyone.
- Recording network games:
-record <name>with-hostor-connectsaves the game you play as a demo that plays back like any other. - Names and colors:
-name Ranger -color 4 12(or the console'sname/color, Options > Shirt Color / Pants Color) — the others see your name on the scoreboard and your player in your shirt and pants colors. - QuakeSpasm mods: Arcane Dimensions and other mods made for QuakeSpasm run with their own QuakeC (Map Browser > a map of the mod) — thousands of entities, fence textures, sprites, translucent entities, their fog, water alpha and music, smoothly animated models and monsters gliding between their steps (
r_lerpmove). - Texture packs:
textures/<map>/<name>.tgaortextures/<name>.tga(or.png) in a mod directory replace a map's textures, with_luma/_glowglow maps, like QuakeSpasm-Spiked. - Map records: every finished level keeps your best time, kills and secrets per skill — the intermission tells you when you beat them, Map Warp shows them.
- Host console: as the host of a network game, type
/status,/kick <name>,/map <name>,/fraglimit 10,/timelimit 5,/teamplay 1or/addbotin the chat line (T). - Teamplay:
-host e1m1 -teamplay 1 -bots 3(or Multiplayer > Team Deathmatch with 3 bots) — teams by pants color, no friendly fire, bots even the teams and the scoreboard shows the team totals. - Spectators:
-connect host -spectate(or Multiplayer > Spectate) watches a network game without playing: fire follows the next player, jump flies freely. - Demos:
.demplayback with server frame interpolation (protocol 15, FitzQuake 666 and RMQ 999 for maps beyond 4096 units), and recording of your own games. While one plays, Space pauses, up / down change the speed (a quarter to four times) and left / right seek 10 s back or forward.
QuakeC mode
progs.datruns the level: a QuakeC VM (pr_exec.csemantics, all builtins) with a world host for traces, movetypes and the player physics. Mods loaded with-pakrun unchanged. Savegames and demo recording work here too.
Multiplayer
- Deathmatch and coop over UDP: the host runs the QuakeC rules, NetQuake protocol 15 messages stream the game to every player, the client is the demo player with input. Host from the menu or with
-host, join with-connect. - Split-screen coop:
-host e1m1 -splitscreen(or Multiplayer > Split-Screen Coop) plays a coop game with two players on one machine, side by side (-splitscreen stacked: one above the other): the keyboard and mouse for the first player, a gamepad for the second. Up to four players (-splitplayers 4, or a further gamepad's Start in a game). In the browser too:?host=e1m1&splitscreen=1(or=4), and bots with?host=e1m1&bots=3. - Deathmatch bots:
-host e1m1 -bots 3(or Multiplayer > Deathmatch vs 3 bots) adds bots that find their way around the map, collect weapons, health and powerups, and fight you and each other;-skillsets how well they aim. - Browser players: a host started with
-websocket 26001(or a separate-wsrelay host:port) relays WebSocket connections to the game, and the web build joins with?connect=hostin its URL (an https page needswss://, so a TLS proxy in front of the relay). - Client-side prediction of your own movement (the server runs exactly the inputs you sent, the client replays the unacknowledged ones) and UDP hole punching through a rendezvous service:
-rendezvouson a reachable machine,-host e1m1 -register myname@rendezvous,-connect myname@rendezvous.
Engine integration
- Custom URL protocols
quakepak:andquaketex:feed the engine's loaders straight from the archives. - Spatial audio with OpenAL, music per map from the CD track number (
svc_cdtrackin demos and multiplayer), muffled under water with an EFX low-pass filter (Web Audio in the browser), static ambient emitters and leaf ambients, OGG music. - Every component is named for the engine's inspector (F8):
func_door_7,monster_ogre_3,light_12... - Developer console (
~):map <name>,god,give all,shadows <0|1>,save,load,debug <all|off|triggers|monsters|movers|leaf>(wireframe overlay of triggers, monsters and sight lines, movers and the player's BSP leaf),wateralpha <0..1>,fog <density> [r g b],sky <name>,fov,gamma,cl_bob,r_drawviewmodel,captions,hud_contrast,reduced_flashes,horde,killall,speedrun,ghost,ghost_reset,saves,maps,maps_install,maps_play,maps_remove,photo,photo_fov,photo_effect,photo_scale,photo_shot,fps,novis <0|1>,help. - A headless test harness (
--autotest,--demo) drives the game by script and takes screenshots; the images above come from it.
Controls
| Key | Action |
|---|---|
| W, A, S, D / Arrows | Move forward, strafe left / right, backward |
| Mouse | Look around |
| Left Mouse / Ctrl | Fire |
| Space | Jump / swim up |
| Shift | Walk (running is the default) |
| E | Use: doors, buttons |
| 1 .. 8 | Weapons: Axe, Shotgun, Super Shotgun, Nailgun, Super Nailgun, Grenade Launcher, Rocket Launcher, Lightning Gun |
| F1 / C | Camera: first person, third person, free fly |
| F2 | Dynamic shadows on / off |
| F6 / F9 | Quicksave / quickload |
| F8 | Engine inspector (component tree, live properties) |
| F12 | Screenshot |
| P | Photo mode (wheel: field of view, Tab: effect, + -: size, Enter: photo) |
| ~ | Developer console |
| Touch | Move stick on the left (where the finger lands), drag on the right to look, FIRE / JUMP / USE / weapon / MENU buttons; on by themselves on touch screens and in the browser after the first touch (-touch, ?touch=1), which also goes fullscreen in landscape; the HUD and menus scale with the phone's pixel density |
| Gamepad | Left stick moves, right stick turns, A jumps, X uses, RB / right trigger fires, D-pad changes weapons, Start opens the menu, View is photo mode; the prompts use your pad's names (Xbox, PlayStation or Nintendo, from its name or Options > Controls) and a Nintendo pad confirms with A; sensitivity, dead zone, inverted look, button names and the confirm button in Options > Controls |
Every key can be rebound in Options > Controls (saved in the user config). | Escape | Menu |
Building and Running
Prerequisites: Castle Game Engine 7.0-alpha.3 or later (the snapshot bundles FPC 3.2.2).
Windows (PowerShell)
$env:PATH = "C:\castle-engine\bin;C:\castle-engine\tools\contrib\fpc\bin;" + $env:PATH
castle-engine compile --mode=release
.\castle-quake1.exeAudio needs OpenAL32.dll and wrap_oal.dll next to the executable (included in the releases).
Linux
export CASTLE_ENGINE_PATH=/path/to/castle-engine
$CASTLE_ENGINE_PATH/tools/build-tool/castle-engine compile --mode=release
./castle-quake1macOS
The releases include app bundles for Intel and Apple Silicon Macs. They are not signed or notarized: on the first start use right-click > Open, or xattr -dr com.apple.quarantine "Castle Quake.app". Building locally is the same castle-engine compile with the engine set up for macOS.
Web
The test job builds tests/quaketests (the BSP tracer, the QuakeC VM, savegames, the demo writer and reader against the shareware pak) and runs it before the packages are released; locally: cd tests && castle-engine compile && ./quaketests ../data/paks.
The Web workflow builds the WebAssembly version with FPC's wasm32 cross compiler and Pas2js, smoke-tests it in headless Chromium (tests/websmoke.js: E1M1 starts, renders, saves, and the save loads after a reload) and deploys it from main to GitHub Pages: mariuz.github.io/castle-quake1. In the browser, savegames, controls and video options are kept in the browser's local storage between visits; ?warp=e1m1 in the page URL starts a map, ?randommap=new a random one.
Command line
castle-quake1 -warp e1m1 Jump to E1M1
castle-quake1 -warp start The LibreQuake hub
castle-quake1 -game quake -warp start The original Quake hub with the episode portals
castle-quake1 -randommap 42 A random level from seed 42 (no seed: a new one)
castle-quake1 -horde -warp e1m1 Horde mode: waves of monsters on E1M1
castle-quake1 -pak custom.pak Load another PAK (mods, the registered id1 paks)
castle-quake1 -playdemo demo1.dem Play a demo from the PAKs
castle-quake1 -qc e1m1 QuakeC mode: progs.dat runs the game
castle-quake1 -game hipnotic -qc start A mission pack or mod (a directory of loose files, pak*.pak and *.pk3) with its QuakeC
castle-quake1 -host start Host a deathmatch game on UDP port 26000 and play in it
castle-quake1 -host e1m1 -coop -skill 2 Host a cooperative game
castle-quake1 -connect 192.168.1.10 Join a game (host[:port])
castle-quake1 -host start -websocket 26001 Host and accept players from the web build (.../play/?connect=yourhost)
castle-quake1 -wsrelay 192.168.1.10:26000 Only a WebSocket relay for a game hosted elsewhere
castle-quake1 --export-map e1m1 e1m1.x3d Export a level as X3D (or .gltf) for the editor / view3dscene
castle-quake1 --autotest e1m1 shot --demo "W:1,S,X,W:0.5,S,Q" Headless test with screenshotsThe Options menu switches the world lighting between Quake's lightmaps and dynamic PBR lighting.
Headless testing
--autotest <map|qc:map|host:map|connect:host|name.dem> <prefix> --demo "<actions>" loads a level without any input, runs the comma separated actions and exits. Actions: W:sec wait, S screenshot, X fire, F:sec hold fire, U use, C:slot weapon, A:deg / T:deg / P:deg yaw, turn, pitch, M:units / G:x;y;z teleport, V:f;s;u move, J jump, K / Y cheats, O:slot / L:slot save and load, R:name / R record a demo, B:1 / B:0 world lighting, I log the scene tree, Q quit. See CLAUDE.md for the details.
Continuous Integration
.github/workflows/build.yml: Windows x86_64, Linux x86_64, macOS x86_64 / aarch64 (app bundle zips) and Android (APK, cross-compilers fromtools/android-fpc.sh) packages on GitHub's runners (FPC and the CGE snapshot set up bycastle-build-ci). A tagv*, or a manual run with arelease_tag, creates the GitHub Release with the packages..github/workflows/web.yml: the WebAssembly build and the GitHub Pages deployment.
License
- Code: MIT License (matching Castle Game Engine and mariuz/castle-doom).
- LibreQuake assets: see
data/paks/COPYING.txtandCREDITS.txt(GPL / BSD / CC). - Quake is a registered trademark of id Software / ZeniMax Media.










