I forked QuakeSpasm, the long-running fork of FitzQuake maintained by Spike, Eric Wasylishen, Ozkan Sezer and others, to see what Claude Code could do under my direction across seven rounds on six old Macs from 1999 to 2019. I drove the methodology and the decisions. Claude wrote every line of C. The 1999 G3 went from unplayable to playable: demo3 at 1024×768 nearly quadrupled (5.10 → 19.80 fps, +288% over the early-port build at commit 4c165e6f), with translucent water, alias-model shadows and emissive-fullbright dynamic lights on. Every fps in this post is the engine's timedemo median of three runs.
The fork is at matthewdeaves/old-mac-quakespasm. Lava, slime and teleporters get the same translucent treatment as water; emissive dynamic lights make buttons, computer screens and tech panels cast coloured light into the room; the on-pickup HUD blink, broken upstream for years, is restored. A round v6 engine fix closes a long-standing X-ray glitch where translucent water used to reveal geometry through pool floors on un-watervis'd id1 maps. The 1999 G3 runs a tighter dynamic-light gate to buy back the muzzle-flash cost on its fragment-shader-less Rage 128; the two G4 Radeons add 16x anisotropic filtering and the GLSL water shader. Jump to the screenshots if you'd rather see it in motion.
#The bench of old Macs
The bench is six machines, named by their Apple codenames in the order they were built:
| Machine | Year | CPU | GPU | macOS |
|---|---|---|---|---|
| yosemite | 1999 | PowerMac G3 B&W, 449 MHz | ATI Rage 128, 16 MB | 10.3.9 Panther |
| sawtooth | 1999 | PowerMac G4 AGP, 500 MHz 7400 | NVIDIA GeForce2 MX, 32 MB | 10.4.11 Tiger |
| quicksilver | 2001 | PowerMac G4, 733 MHz 7450 | Radeon 9000 Pro, 64 MB | 10.4.11 Tiger |
| mini-g4 | 2005 | Mac mini G4, 1.25 GHz 7447A | Radeon 9200, 32 MB | 10.4.11 Tiger |
| mini-intel | 2007 | Mac mini, 2.33 GHz Core 2 Duo | Intel GMA 950 | 10.7.5 Lion |
| imac-2019 | 2019 | iMac 27", 3.7 GHz i5-9600K | Radeon Pro 580X, 8 GB | 15.7.5 Sequoia |
The four PowerPC machines and the Lion-era Intel mini are the interesting targets. The 2019 iMac is along for the ride as a sanity-check baseline — same source tree, same fat binary, but with a current GPU that turns Quake into something it doesn't have to work for. It runs Quake at about 2000 fps; the bottleneck is the CPU side of the engine, not the graphics card.
The mini-intel pulls double duty: it's both a benchmark target and the cross-build host for every PPC slice, because it's the only modern-ish Mac that still ships Apple's PowerPC-targeting GCC 4.0.1 alongside the 10.3.9 and 10.4u SDKs. The whole bench sits next to the Ubuntu workstation, driven over ssh — I'm not clicking around any of the Mac UIs by hand, but I'm sitting next to them while they run.
The rule for the project is best looking, comfortably playable; never sacrifice visuals for fps. The playability floors are 60 fps on the G4s and Lion, 20 fps on the G3, and on the iMac there isn't one.
#The build and devops setup
Six old Macs benched against every code change sounds like it ought to involve sitting in front of one at a time with a keyboard and a stack of patience. It doesn't. The Macs are fully automated bench targets — they run the timedemos, write qconsole.log, and reboot themselves when needed — driven over ssh from the Ubuntu workstation. Displays stay on so I can watch the demos play, but I never click around any of the Mac UIs by hand. The Linux box is the orchestrator: git repo, Claude Code session, every build/deploy/bench script.
You can't compile a binary that runs on a 1999 PowerPC G3 from a modern Linux box — you need a real Apple toolchain pointed at a real Apple SDK. So when Claude lands a code change on Ubuntu, the build flow is: rsync sources up to the Lion mini, ssh in to run make against the right SDK, scp the resulting binary back. Three slices come out:
- g3 — PPC 750, 10.3.9 SDK, no AltiVec.
- g4 — PPC 7400, 10.4u SDK, AltiVec on. One binary serves all three G4s.
- lion — x86_64 against Lion's clang. Runs on both Intel Macs.
build-fat.sh runs the three sub-builds serially and lipo -creates them into one fat Mach-O. That binary plus one Quakespasm.app bundle ships to all six Macs unchanged, and dyld picks the right slice on each host. Within a slice (the Lion x86_64 build runs on both the mini-intel and the iMac, the G4 build runs on three different G4s), the engine queries sysctlbyname("hw.model", …) at boot and Cbuf_AddTexts the matching autoexec-<machine>.cfg from inside the bundle — that's how one fat binary truly serves all six machines, each with its own visual stack.
lipo -create stitches the three Mach-O slices together so each Mac picks the one for its CPU at launch.No CI, no containers, no Docker — plain rsync + ssh + scp on top of make. The one gotcha is that build-fat.sh has to hold a flock: running the three sub-builds in parallel races on the shared source tree on Lion, so they're serialised.
#The bench loop
Quake ships with three demo files (demo1, demo2, demo3) and a built-in timedemo command that plays back a demo as fast as the engine can render it, then prints the average frames per second to the console log. That gives a deterministic, repeatable number for any given (machine, demo, resolution, code) combination. A "cell" in this project is one such combination.
Each cell runs three independent timedemos; the median of runs 2 and 3 lands in the CSV (run 1 is warm-up and dropped). Polling uses integer sleep because Panther's /bin/sleep from 2003 can't parse fractional seconds. The console log is written through raw write() with no stdio buffering, which is why polling it for a regex match actually works.
Reliable shutdown matters because the early rounds taught me what hard crashes do to a 1999 Mac. A bad code change on the G3 would freeze the engine in a state where the OS was still up but I couldn't see anything, so my only option was a hardware reset — and twice that corrupted enough of Panther's filesystem on the SSD that I had to reinstall OS X 10.3 from the install discs. Every kill in the bench scripts now goes TERM-then-grace-then-KILL and SDL_Quit is given time to unwind the OpenGL context, because Panther's Rage 128 driver leaves the display LUT in a corrupt state if the process is yanked mid-frame. The screen goes dark or shows garbage, the OS is alive, but you can't see what you're doing — and if you reach for the power button you might be reinstalling the OS for the next hour.
parallel-bench.sh fans the loop out across all six machines at once. Six concurrent ssh sessions, six concurrent timedemos, six concurrent log polls, all appending to the same CSV. Row appends are atomic on Linux under PIPE_BUF (4 KB) and a row is about 80 B, so concurrent legs don't interleave. Wall time is roughly the slowest leg — the G3, which takes minutes where the iMac takes about a second — so a full sweep is about as long as benching the G3 alone. (The dedicated runner matters: kicking off six manual bench.sh legs from one shell degrades the G3 reading, so the parallel sweep goes through this script.)
A second runner, screenshot.sh, drives the engine's screenshot tga console command through demo1/2/3 across all machines and rsyncs the per-machine galleries back; that's how the screenshot grids further down get captured. And qsreboot.sh is the unattended-recovery tool that issues an OS-level shutdown -r over ssh and waits for the machine to come back up — load-bearing on yosemite when the Rage 128 LUT corruption flips the screen black mid-sweep.
While a sweep runs, all six Macs play the demos in unison on the bench with the sound on: six chassis humming together, six idle notes, the Tiger and Panther boot chimes if anything triggers a reboot. I watch them play through while the terminal collects the rows.
#Claude Code tooling
Claude Code does the actual coding. To make that productive across seven rounds the repo carries scaffolding every fresh session inherits.
CLAUDE.md captures the durable knowledge a session needs up front: hardware inventory, naming conventions, the cvars roll for per-target toggles, the rule that every change ships behind an opt-in flag. MISTAKES.md is append-only and records every approach we tried and reverted with dates and reasoning. New sessions read both before proposing a change.
On top of those, a ppc-ops skill tells Claude when and how to invoke the build, deploy and bench scripts, and two slash commands wrap them with the right arguments — /bench mini-g4 demo3 1024x768 for one cell, /bench all for the parallel sweep, /deploy quicksilver to build and ship one slice. Together they stop Claude from inventing inline ssh + make heredocs every time a change needs benching, and post-run summaries include a 3% threshold check for "is this a real win".
The static-analysis battery is plumbed in similarly. analysis/ holds per-tool log files and a triage index, and the analysers run on a Linux build target compiling the same source the PPC and Lion binaries do — cppcheck, clang-tidy, scan-build, gcc -fanalyzer, flawfinder, sparse, iwyu, shellcheck, ASan and UBSan against a headless timedemo. Apple's gcc 4.0.1 from 2007 doesn't flag any of the things gcc 15 does, so the Linux target catches a different class of issue.
A typical session: I describe a phase, Claude proposes a plan, runs the analysers, drafts the change, drives the bench loop across all six machines, commits the code change separately from the bench commit, and writes the MISTAKES.md entry if the experiment didn't pan out. I read the diffs, sanity-check the numbers, and decide what to ship.
#Frames per second across the rack
Numbers are the round v7 wrap full-grid bench at commit f2df151d. Every cell is the median of three runs (run 1 dropped as warm-up).
Current build, six machines, all three demos. Each cell is fps; column headers are demo × screen resolution (1024×768 or 640×480).
| Machine | demo1 1024×768 | demo1 640×480 | demo2 1024×768 | demo2 640×480 | demo3 1024×768 | demo3 640×480 |
|---|---|---|---|---|---|---|
| yosemite (G3) | 16.55 | 35.20 | 15.20 | 33.40 | 19.80 | 36.75 |
| sawtooth (G4) | 42.65 | 55.90 | 35.40 | 55.10 | 46.75 | 57.30 |
| quicksilver (G4) | 64.20 | 71.95 | 62.45 | 72.10 | 86.15 | 98.25 |
| mini-g4 (G4) | 49.40 | 86.50 | 39.30 | 74.80 | 68.20 | 114.35 |
| mini-intel (Lion) | 73.05 | 165.35 | 54.55 | 132.15 | 44.70 | 189.00 |
| imac-2019 | 1835.25 | 2048.60 | 1853.25 | 2018.55 | 1731.70 | 1807.00 |
What the work bought. Below is the BEFORE → AFTER for every cell where there's a clean pre-optimisation baseline. The "before" rows are the earliest bench captured for each machine; the "after" rows are the v7 wrap binary with the full per-machine visual stack on (translucent water, alias-model shadows, GLSL water on the programmable G4s and the iMac, 16x anisotropic filtering, gl_texture_lodbias -1.5, emissive-fullbright dynamic lights — none of which the early build had).
| Machine | Demo | Resolution | Before fps | After fps | Δ |
|---|---|---|---|---|---|
| yosemite (G3) | demo1 | 1024×768 | 7.70 | 16.55 | +115% |
| yosemite (G3) | demo3 | 1024×768 | 5.10 | 19.80 | +288% |
| sawtooth (G4) | demo3 | 1024×768 | 39.45 | 46.75 | +18.5% |
| sawtooth (G4) | demo3 | 640×480 | 43.95 | 57.30 | +30.4% |
| imac-2019 | demo3 | 1024×768 | 1544.80 | 1731.70 | +12.1% |
| imac-2019 | demo3 | 640×480 | 1714.00 | 1807.00 | +5.4% |
| mini-g4 (round v6→v7) | demo3 | 1024×768 | 47.90 | 68.20 | +42.4% |
| mini-g4 (round v6→v7) | demo3 | 640×480 | 78.40 | 114.35 | +45.9% |
The G3 demo3 number is the headline: 5.10 → 19.80 fps, +288%. Quake on a 449 MHz G3 with a 16 MB Rage 128 sits at the 20 fps comfort floor instead of well below it. The shipping default for the G3 is now 640×480 (commit e0c66f57), where demo1, demo2 and demo3 all run at 33–37 fps with the same visual stack on. 1024×768 is still there via the console (vid_width 1024; vid_height 768; vid_restart). The plain-English version: the early port at 1024 was 5–8 fps unplayable, the v7 build is 15–20 fps at 1024 and 33–37 fps at the 640 default. Round v7's surprise came on the 2005 mini G4. Phase 1's DrawGLPoly sky-state client-state hoist landed +42% / +46% on demo3 there because the Radeon 9200's ATI driver pays a steep penalty for the per-call glEnableClientState / glDisableClientState traffic the earlier sky path was issuing. Same patch was within-noise on every other GPU.
Demo1 and demo2 are where the build trades headline fps for the visual stack the early build didn't have. Sawtooth's demo2 1024 went 54.60 → 35.40 against its vanilla 1615d99a baseline — the cost of four alpha cvars on a 1999 fixed-function GPU. The iMac is the same shape: demo1 1024 went 2042 → 1835, paying about 10% to render translucent liquid, alias-model shadows and emissive dlights. Every machine still clears its comfort floor on every cell. Three of the six (quicksilver, mini-g4, mini-intel) joined mid-project, so they don't have a clean pre-optimisation baseline. Quicksilver's nearest equivalent is the 1615d99a baseline at demo1 640 windowed = 127.10 fps. Mini-g4 only has a v6→v7 delta because its first reliable rolling-CSV row is the v6 wrap.
#Visual stack, per machine
The other side of the work is what each machine actually renders. Most rows in the table below are upstream cvars set per machine via an autoexec inside the .app bundle. The fork adds four cvars (r_dynamic_distance, r_shadow_distance, r_emissive_lights, gl_texture_lodbias) plus a v6 engine fix that makes translucent water render correctly on un-watervis'd id1 maps. The HUD pickup-blink row is a regression fix in upstream code; everything else is configuration.
| Feature | Origin | yosemite (G3) | sawtooth (G4 fixed-fn) | quicksilver (G4 prog.) | mini-g4 (G4 prog.) | mini-intel (Lion) | imac-2019 |
|---|---|---|---|---|---|---|---|
Translucent water (r_wateralpha 0.6) | upstream cvar (default 1) | yes | yes | yes | yes | yes | yes |
| Translucent lava/slime/teleporters | upstream cvars (default 0 = fall back to r_wateralpha, opaque) | yes | yes | yes | yes | yes | yes |
| Translucent water without X-ray (NoVisPVS trigger) | new in fork (v6) | yes | yes | yes | yes | yes | yes |
Alias-model shadows (r_shadows 1) | upstream cvar (default 0) | yes | yes | yes | yes | yes | yes |
| Shadow distance gate | new in fork | — | 512 | 512 | 512 | 512 | unlimited |
| Dynamic light distance gate | new in fork | 768 | 768 | — | — | — | — |
Emissive-fullbright dlights (r_emissive_lights) | new in fork (v7) | radius 0.5, max 4 | radius 0.5, max 6 | radius 1.0, max 12 | radius 1.0, max 12 | radius 0.75, max 8 | radius 1.5, max 32 |
| GLSL water shader | upstream (r_oldwater 0) | — | — | yes | yes | — | yes (driver path) |
| 16x anisotropic filtering | upstream cvar (default 1) | — | — | yes | yes | yes | yes |
Trilinear filtering (GL_LINEAR_MIPMAP_LINEAR) | upstream cvar | — | yes | yes | yes | yes | yes |
Sharper distant mips (gl_texture_lodbias -1.5) | new in fork (v4) | — | yes (likely inert on GeForce2 MX) | yes | yes | — | — |
Skip framebuffer clear (gl_clear 0) | upstream cvar (default 1) | yes | — | yes | yes | yes | yes |
| Default ship resolution | autoexec | 640×480 (since e0c66f57) | 1024×768 | 1024×768 | 1024×768 | 1024×768 | 2560×1440 |
| HUD pickup blink restored | fork bug-fix | yes | yes | yes | yes | yes | yes |
The yosemite column also picks up two G3-only perf trims that aren't in the table because no other machine uses them: gl_subdivide_size 256 (coarser warp tessellation, ~4× fewer triangles per warp surface, matters because the Rage 128 is per-vertex-cost-sensitive 1999 silicon) and r_particles 2 (square particles instead of triangle splats — CPU-cheaper, no visual difference at distance).
Translucent water/lava/slime/teleporters. The four alpha cvars all ship in upstream QuakeSpasm but default to opaque. Setting them to 0.6 per machine makes those surfaces show the geometry underneath — the single biggest visible difference between the upstream defaults and what these Macs can do. On the Radeon 9000/9200 it's a single fragment-uniform read in the existing GLSL water path; on the fixed-function GPUs (Rage 128, GeForce2 MX, GMA 950) it's a textured warp with per-pixel alpha blending. Cost on every machine landed inside the noise floor.
Translucent water without the X-ray glitch (new code in the fork, v6). Setting the alpha cvars below 1 on stock id1 maps exposes a long-standing upstream bug: BSPs were compiled assuming opaque water, so leaves on the far side of a liquid surface aren't in PVS. The renderer ends up blending translucent water against whatever's in the framebuffer behind it (sky, void, distant unrelated geometry) — that's the X-ray-through-floors and flicker on e1m1 pools when r_wateralpha < 1. Round v6 ports Spike's Mod_FindContentsTransparent from Ironwail and adds a trigger in R_MarkSurfaces that drops to Mod_NoVisPVS whenever a liquid alpha is below 1 and the worldmodel isn't already vis'd transparent. Cost is one full world-mark per frame on un-vis'd maps. Watervis-patched custom maps stay on the optimal-PVS path.
Skipping the per-frame framebuffer clear. gl_clear defaults to 1 upstream. Setting it to 0 was the biggest single-pass perf win of round v5: Quake's world plus sky covers 100% of the screen in normal play so the per-frame glClear(GL_COLOR_BUFFER_BIT) is redundant fillrate work. Z-buffer correctness is preserved. +9.3% on mini-g4 demo3 1024 (the biggest win on the matrix), +7.1% on quicksilver, +4.7% on the G3, +3.5% on Lion — and a 2.1% regression on sawtooth. Sawtooth's GeForce2 MX driver has a slow no-clear path, unique among the five PowerPC-era GPUs, so sawtooth alone keeps gl_clear 1.
Dynamic-light distance gate (new code in the fork). The Rage 128 has no fragment shaders, so each dynamic light is a full extra blending pass over the affected surfaces. On a 449 MHz G3, a single muzzle flash turns into a GPU-bound stutter. Claude added r_dynamic_distance (default 0, engine behaviour preserved) that skips dlights past a squared-distance threshold; the check is in R_PushDlights. The G3 sets it to 768, buying back +5.4% on demo3 640. The G4s and Lion ship with the cvar at 0 because they don't pay the same cost.
Shadow distance gate (new code in the fork). Same idea for alias-model shadows, round v3 — r_shadow_distance set to 512 on sawtooth, quicksilver, mini-g4 and mini-intel so distant model shadows stop costing fillrate. The G3 leaves the gate at unlimited (alias-model shadows are cheap on a Rage 128 once you're not also paying the per-dlight cost), and the iMac has the headroom to draw every shadow at any distance.
Emissive-fullbright dynamic lights (new code in the fork, round v7). Quake's wall textures carry "fullbright" pixels — the bright bits on buttons, fluorescent fixtures, computer screens, tech panels — that the engine has always rendered as glow without any actual light spilling into the room. gl_emissive.c walks the world's brush surfaces at map load, name-filters textures (light/lite/button/btn/basebutn/comp/tech/panel/screen/switch/exit), computes seed positions, and stores up to 128 of them. Per frame, R_PushEmissiveLights picks the nearest N seeds within r_dynamic_distance, applies the per-machine radius scale, and injects up to r_emissive_lights_max into cl_dlights[] — the same pipeline muzzle flashes use, no GLSL, so it works on every GPU in the rack. Three new cvars (r_emissive_lights, r_emissive_lights_radius, r_emissive_lights_max), default off in code; the per-machine autoexecs turn them on with radius and max counts tuned to the GPU, so the build ships with emissive lighting on across all six machines.
Sharper distant mips on the programmable G4s (new cvar in the fork, round v4). Quicksilver and mini-g4 ship with gl_texture_lodbias set to -1.5. The Radeon driver picks a coarser mip than it should at oblique angles, so distant brick walls and slipgate signs go soft. A negative LOD bias pulls the sampler toward sharper mips — visibly cleaner at distance with no aliasing crawl at -1.5.
HUD pickup-blink restore (bug fix). Upstream had unconditionally killed the on-pickup blink for keys, runes, sigils and hipnotic/rogue items, leaving four downstream && flashon branches as silent dead code. The 5 Hz blink phase from cl.time is restored at sbar.c:657; the inventory icons twinkle when you pick up a sigil again.
#Screenshots from each machine
These are demo captures from the bundled fat binary running on each machine in turn — round v7 timestamp 20260509T155106Z. Same source tree, same three demos. The visual stack varies per target (different GPU classes, different cvar tunings) and these shots lean toward action, lighting, and the visible results of the v6 watervis fix.
# yosemite — PowerMac G3 B&W, ATI Rage 128, 1999
The hardest machine in the matrix. 449 MHz G3, 16 MB Rage 128, no fragment shaders, no AltiVec. Ships with translucent water/lava/slime/teleporters, alias-model shadows on, emissive-fullbright dlights (radius 0.5, max 4 — tightest in the matrix because every dlight on the Rage 128 is a full extra blend pass), dynamic-light distance gate at 768 (the project's headline G3 fix), classic warp water, framebuffer-clear skip. Default ship resolution is 640×480 since e0c66f57 — at 640 every demo runs at 33–37 fps; 1024×768 stays available via console for users who want it (demo3 sits right on the 20 fps floor at that resolution).
# sawtooth — PowerMac G4 AGP, GeForce2 MX, 1999
Fixed-function G4. Same generation as the G3 in GPU class — no fragment shaders — but with AltiVec and a faster bus. Ships with translucent water, alias-model shadows (flipped on in commit f3bea2f2 for visual parity with the G3 — the per-machine bench is pending so the FPS table above predates this), shadow distance gate at 512, dynamic-light distance gate, emissive-fullbright dlights (radius 0.5, max 6), trilinear filtering, and the framebuffer-clear cvar at 1 (the GeForce2 MX driver alone has a slow no-clear path).
# quicksilver — PowerMac G4 733 MHz, Radeon 9000 Pro, 2001
The G4 with the most visual headroom. Programmable pipeline, GLSL water shader, 16x anisotropic filtering, trilinear, sharper distant mips at gl_texture_lodbias -1.5, alias-model shadows on with 512-unit distance gate, emissive-fullbright dlights at radius 1.0 / max 12. Of the four PowerPC machines this is the one that looks closest to a modern Quake build at 1024x768.
# mini-g4 — Mac mini G4 1.25 GHz, Radeon 9200, 2005
Inherits the Quicksilver visual stack — same GLSL water, same shadow gate, same anisotropic filtering, same gl_texture_lodbias -1.5, same emissive radius/max (1.0 / 12). Its Radeon 9200 is the same generation as the 9000 in the Quicksilver, so the visual character is essentially identical. The mini's higher clock (1.25 GHz vs 733 MHz) shows up in framerate, not in pixels.
# mini-intel — Mac mini Core 2 Duo, GMA 950, Lion, 2007
Intel hardware but with the GMA 950, which has no GLSL water path of its own — so it falls back to the classic warp like the PowerPC fixed-function GPUs. Anisotropic filtering at 16x, trilinear, shadows on at 512-unit gate, framebuffer-clear skip on, emissive-fullbright dlights at radius 0.75 / max 8 (dialled back from the G4 trio because GMA 950 is fillrate-modest). No gl_texture_lodbias because the GMA's GL 1.4 driver doesn't reliably expose EXT_texture_lod_bias. Fillrate-bound at 1024 on demo3 but easily breaks 200 fps at 640.
# imac-2019 — iMac 27" 5K, Radeon Pro 580X, Sequoia
The modern outlier, captured at 2560×1440 native. Same Lion-target binary as the mini-intel (the engine still uses the GL 1.x fixed-function path because it links against the 10.7 SDK from 2011), but the GPU has so much headroom that the render is silky and the demo runs at ~2000 fps. Visual stack pushed to the maximum: aniso 16, trilinear, shadows on with no distance gate (r_shadow_distance 0 = unlimited), r_dynamic_distance 0 = unlimited, all four alpha cvars on, gl_clear 0, emissive-fullbright dlights at the matrix-widest radius 1.5 / max 32. Useful as the sanity-check baseline against which the cumulative CPU-side work shows up.
#After seven rounds
The fun bit was hearing it. Six machines running the same demos, the PowerMacs humming with their fans up, speakers chiming through pickups and explosions, all of them whipping through the timedemos one after another. The Lion mini sat in the middle of it building the fat binary and orchestrating each bench machine over SSH, which is its own quiet bit of devops satisfaction.
The 1999 G3 going from unplayable to playable is the big one, but all six machines now run with translucent water, alias-model shadows and emissive dynamic lights on. The game looks fantastic on every one of them.
Quake II next, I think. Maybe some other games from when I was a kid.
#Get the build
The current release is v1.1-round-v7 — round v7 wrap plus emissive lights enabled per machine. Four download bundles:
| Bundle | Size | Runs on |
|---|---|---|
| Quakespasm-fat-universal-ppc750-ppc7400-x86_64.zip | 4.9 MB | All six benched Macs (recommended) |
| Quakespasm-ppc750-Panther.zip | 4.0 MB | G3 / 10.3.9 Panther+ |
| Quakespasm-ppc7400-AltiVec-Tiger.zip | 4.0 MB | G4 / 10.4 Tiger+ |
| Quakespasm-x86_64-Lion-or-newer.zip | 3.9 MB | Intel / 10.7 Lion+ (verified to Sequoia 15.7) |
The fat universal binary is one Mach-O with all three slices stitched together, so it runs unchanged on Panther 10.3.9 through Sequoia 15.7 — dyld picks the right slice on each host. The bundle expects to live at ~/Desktop/quake/; release notes have full install steps and Gatekeeper handling.