Optimising Quake on six old Macs

I forked QuakeSpasm to see if I could use Claude Code to get a build tuned for six old Macs spanning 1999 to 2019 — PowerPC G3, three G4s, a 2007 Intel Core 2 Duo on Lion, and a 2019 iMac as the modern baseline. One fat binary, one app bundle, a per-machine autoexec on each host.

QuakeSpasm on yosemite (1999 PowerMac G3, ATI Rage 128) — grenade in mid-air with explosion sparks
yosemite — 1999 PowerMac G3 / Rage 128 / Panther
QuakeSpasm on quicksilver (2001 PowerMac G4 733 MHz, Radeon 9000) — gibbed grunts on the floor with another in the doorway
quicksilver — 2001 PowerMac G4 / Radeon 9000
QuakeSpasm on mini-g4 (2005 Mac mini G4, Radeon 9200) — overhead view of an Ogre with chainsaw and visible alias-model shadow
mini-g4 — 2005 Mac mini G4 / Radeon 9200
QuakeSpasm on imac-2019 (2019 iMac 5K, Radeon Pro 580X) — slipgate hub with Q logo on floor
imac-2019 — 2019 iMac 5K / Radeon Pro 580X

# The bench

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 setup

Six headless Macs driven over ssh from an Ubuntu workstation. The Lion mini is both a bench target and the cross-build host — it's the last Mac that ships PPC-targeting GCC 4.0.1 alongside the 10.3.9 and 10.4u SDKs.

Architecture diagram showing Ubuntu workstation orchestrating six Mac bench targets via the Lion mini cross-build host
Architecture — Ubuntu orchestrator drives six Macs via the Lion mini cross-build host

Three compilers build three CPU-specific binaries. Apple's lipo tool glues them together into one universal file; on each Mac, macOS picks the slice for its CPU at launch time. Plain rsync + ssh + scp on top of make.

Build pipeline diagram showing three toolchains (g3 SDK, g4 SDK, lion clang) converging through lipo into one fat binary
Build pipeline — three compilers, three CPU-specific binaries, one universal file

Quake's built-in timedemo plays a recorded demo as fast as the engine can render it and prints the fps to the console log. Each bench run is one combination of machine, demo, and resolution. Three runs per combination, the first dropped as a warm-up, median of the other two lands in the CSV. parallel-bench.sh runs the whole grid across all six machines in parallel.

Bench loop showing deploy, launch with timedemo, poll qconsole.log, parse fps, append to results.csv
Bench loop — deploy, launch timedemo, parse fps, append to CSV

# Before and after

Both rows are demo timings on the 1999 G3 — before, and after the work with the full visual stack on (see-through water, drop-shadows under monsters, glowing dynamic lights).

Machine Demo Resolution Before fps After fps Δ
yosemite (G3) demo3 1024×768 5.10 20.95 +311% (4.1×)
yosemite (G3) demo1 1024×768 7.70 17.35 +125% (2.3×)
yosemite (G3) demo3 640×480 15.60 36.85 +136% (2.4×)
yosemite (G3) demo1 640×480 23.90 34.45 +44%

Only the G3 row is positive across every cell — every round on G3 chased fps and nothing else. The other rows in the full earliest-to-current grid are mixed because each machine's first build was plain stock QuakeSpasm with the engine's default settings, and the project has stacked visual upgrades on top since then — 16× anisotropic filtering, smoother texture filtering, drop-shadows under monsters, see-through water and lava and slime and teleporters, glowing dynamic lights. The negative cells are deliberate visual trade-offs, not regressions — every feature landed in a commit that measured the fps cost across the matrix and weighed it against the visual gain.

# Frames per second across the rack

Round v11.1 full-grid bench at commit d64427db. Median of three runs per cell. Round v11.1 added a per-frame GL state cache in R_DrawAliasModel that lands +19% to +46% on demo3 1024 across the G4/Intel/iMac slices; the G3 ppc750 slice compiles it out via QS_DISABLE_ALIAS_STATE_CACHE because same-session A/B showed a regression on the Rage 128.

Machine demo1
1024×768
demo1
640×480
demo2
1024×768
demo2
640×480
demo3
1024×768
demo3
640×480
yosemite (G3) 17.35 34.45 15.25 33.40 20.95 36.85
sawtooth (G4) 40.25 55.65 32.70 50.15 46.90 57.55
quicksilver (G4) 62.75 70.30 60.10 68.00 84.05 95.35
mini-g4 (G4) 48.45 86.30 37.40 73.85 65.60 113.20
mini-intel (Lion) 72.85 163.95 54.50 130.70 44.60 185.90
imac-2019 1610.95 1894.45 1490.20 1876.20 1575.15 1907.25

# Screenshots from each machine

# yosemite — PowerMac G3 B&W, ATI Rage 128, 1999

yosemite — three grunts patrolling the demon-banner corridor with the player wielding a grenade launcher
banner corridor, three grunts on patrol
yosemite — grenade in mid-air with explosion sparks against a torch-lit stone wall
grenade mid-flight, sparks and torch light
yosemite — underwater pool view showing the stone passage below the surface
underwater pool, see-through water surface
yosemite — pillared hall with three torches glowing in alcoves and arches receding into darkness
pillared hall, torch-lit alcoves
yosemite — combat in a tall stone room, ogres in doorways and grenade-explosion sparks raining through the air
combat scene, ogres and grenade sparks
yosemite — Quake Q-rune logo banner glowing in the slipgate area
Q-rune banner, slipgate area

# sawtooth — PowerMac G4 AGP, GeForce2 MX, 1999

sawtooth — pillared hall with a distant grunt and ammo box, gibs on the floor
pillared hall, distant grunt
sawtooth — torch-lit pillared corridor, super-shotgun in hand
torch-lit corridor
sawtooth — pool surface seen from above with stonework visible underneath
translucent pool, see-through water
sawtooth — vertical wooden room interior with overhead torch and ledges
vertical room with torch lighting
sawtooth — Quake Q-rune logo banner glowing in the slipgate area
Q-rune banner, slipgate area
sawtooth — chaotic combat scene with explosions, gibs, and dynamic-light effects
combat with explosion lighting

# quicksilver — PowerMac G4 733 MHz, Radeon 9000 Pro, 2001

quicksilver — looking down at an ammo crate in a wooden room with mossy stone walls
opening corridor, ammo crate
quicksilver — gibbed grunts on the floor with another grunt visible in the doorway
gibs aftermath, grunt in doorway
quicksilver — pillared corridor with torches and an enemy in the distance
pillared corridor, distant enemy
quicksilver — looking up at a wooden ceiling section, mossy stone walls and an open ledge above
vertical room, looking up
quicksilver — slipgate hub with sparks, the Q logo on the floor, glowing E letters and ornate carved walls
slipgate hub, sparks and runes
quicksilver — slipgate area with the Q-rune floor logo, glowing E sigils and a distant torch
Q-rune floor, glowing sigils

# mini-g4 — Mac mini G4 1.25 GHz, Radeon 9200, 2005

mini-g4 — opening corridor with ammo crate, mossy stone walls
opening corridor
mini-g4 — gibbed grunts on the floor with another grunt visible in the doorway
gibs aftermath
mini-g4 — pillared corridor with torches and a distant enemy, super-shotgun in hand
pillared corridor, distant enemy
mini-g4 — pool surface seen from above with stonework visible underneath through translucent water
see-through pool, stonework below
mini-g4 — vertical wooden room interior with overhead torch and stairs
vertical room, torch lighting
mini-g4 — overhead view of an Ogre wielding a chainsaw, with a visible alias-model shadow on the tiled floor next to the Q-rune teleport pad
Ogre with visible alias-model shadow

# mini-intel — Mac mini Core 2 Duo, GMA 950, Lion, 2007

mini-intel — opening corridor with ammo crate, mossy stone walls
opening corridor
mini-intel — banner corridor with four grunts patrolling, demon banner on the wall
banner corridor, four grunts
mini-intel — pillared corridor with torches and a distant enemy
pillared corridor, distant enemy
mini-intel — pool surface seen from above with stonework visible through the translucent water
see-through pool
mini-intel — vertical wooden room interior with overhead torch and stairs
vertical room with torch
mini-intel — slipgate hub looking down at the Q-rune floor logo and ornate carved walls
Q-rune slipgate

# imac-2019 — iMac 27" 5K, Radeon Pro 580X, Sequoia

iMac 2019 — banner corridor at 1440p with four grunts and a demon banner on the wall
banner corridor, four grunts
iMac 2019 — pillared room interior at 1440p, ammo crate visible
pillared room, ammo
iMac 2019 — armoured pillared hall with armor pickup, ammo box and a distant grunt
pillared hall, armor pickup
iMac 2019 — vertical wooden room with overhead torch and stairs
vertical room with torch
iMac 2019 — overhead view of the slipgate hub showing the Q-rune floor logo and ornate carved walls
slipgate Q-rune from above
iMac 2019 — slipgate hub at 1440p showing the Q-rune floor, ornate carved walls and glowing E-letter sigils
slipgate hub, glowing sigils

# Claude Code setup

CLAUDE.md is the durable knowledge a session needs: the hardware inventory, naming conventions, per-Mac engine-setting rules, and the rule that every change ships behind an opt-in flag (off by default until it's proven its worth). MISTAKES.md is append-only — every approach tried and reverted, with dates and reasoning.

A ppc-ops skill tells Claude when to invoke the build, deploy and bench scripts. Two slash commands wrap them: /bench mini-g4 demo3 1024x768 for one single run, /bench (no arguments) for the parallel sweep across all six machines, and /deploy quicksilver to build and ship one slice to a named Mac.

Static analysis runs 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 what gcc 15 does, so the Linux target catches a different class of issue.

# Get the build

The current release is v1.2-round-v8.

Bundle Size Runs on
quakespasm-fat-app-v0.97.0.zip 4.8 MB All six benched Macs (Panther 10.3.9 → Sequoia 15.7)

The bundle expects to live at ~/Desktop/quake/; release notes have full install steps and Gatekeeper handling.