Quake
id Software's 1996 first-person shooter, read through id's GPL release of the engine source and the QuakeC game code.
On this page
The atlas reads Quake through the engine source id Software released under the GPL, WinQuake’s version 1.091,1, and through the 1.01 QuakeC, the game code that holds the jump2. The 1996 DOS executable is not in the release, so every claim here describes WinQuake, and QuakeWorld only where a claim names it.
Movement runs once per host frame, at most 72 frames/s3,3. On the ground, an acceleration of 10 outpaces a friction of 4, so a run settles at the 320 cap4,5,6. In the air the push stops once the speed along the wish direction reaches 30, but a velocity pointing elsewhere still takes it; turning the view to keep a strafe at right angles to the motion adds speed every frame, the root of air strafing7,8. A jump adds 270 units/s2,2 against a gravity of 800 units/s²9,9. Friction runs before the jump in each frame, so a bunny hop loses one frame of ground friction per landing, which QuakeWorld’s reordered code avoids10,11.
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Running | Momentum | acceleration 10 against friction 4 settles a run at exactly the 320 cap, reached in about a seventh of a second |
| Strafe-running | Air strafe | on the ground a strafe only helps a walk; in the air the push along the wish direction stops at 30, so turning into the strafe keeps adding speed |
| Bunny hopping | Landing friction | friction runs before the jump in each frame, so every hop spends one frame under ground friction, which QuakeWorld's movement code skips |
| Jump | One speed | the QuakeC adds 270 units/s upward, and only after the button has been released since the last jump |
| Gravity | One gravity | 800 units/s² every frame out of water, 100 on E1M8 |
| Air control | Momentum | the move keys push against the speed carried, but only up to 30 along the wish direction, with no friction in the air |
| Demo playback | Recorded state | the server's messages with the client's view angles, read in place of the network; no server runs |
Every claim about this game, by concept (18)
References
- ^ abid Software's released Quake engine source numbers its single-player engine, WinQuake, as version 1.09 (
VERSIONinquakedef.h); the readme calls the release "the complete source code for winquake, glquake, quakeworld, and glquakeworld" and says the original DOS version "should also be buildable from these sources, but we didn't bother trying".Codeid-Software/QuakeHow we know
26#define VERSION 1.09WinQuake/quakedef.h line 26 at bf4ac42 · upstream ↗ 2This is the complete source code for winquake, glquake, quakeworld, and 3glquakeworld.
readme.txt lines 2–3 at bf4ac42 · upstream ↗ 13The original dos version of Quake should also be buildable from these 14sources, but we didn't bother trying.
readme.txt lines 13–14 at bf4ac42 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abcQuake's jump is QuakeC (1.01's
client.qc): each framePlayerPreThinkcallsPlayerJumpwhile the jump button is held and otherwise setsFL_JUMPRELEASED; on land (water level under 2, not in a water jump),PlayerJumpacts only when the player is on the ground and has released the button since the last jump, then clears both flags and adds 270 to the vertical velocity. Holding the button never jumps twice.Codeid-Software/Quake-ToolsHow we know
972 if (self.button2) ⋮ 974 PlayerJump (); ⋮ 976 else 977 self.flags = self.flags | FL_JUMPRELEASED;
qcc/v101qc/client.qc lines 972–977 at c0d1b91 · upstream ↗ 773 if (self.flags & FL_WATERJUMP) 774 return; ⋮ 776 if (self.waterlevel >= 2)
qcc/v101qc/client.qc lines 773–776 at c0d1b91 · upstream ↗ 798 if (!(self.flags & FL_ONGROUND)) 799 return; ⋮ 801 if ( !(self.flags & FL_JUMPRELEASED) ) 802 return; // don't pogo stick ⋮ 804 self.flags = self.flags - (self.flags & FL_JUMPRELEASED); ⋮ 806 self.flags = self.flags - FL_ONGROUND; // don't stairwalk ⋮ 811 self.velocity_z = self.velocity_z + 270;
qcc/v101qc/client.qc lines 798–811 at c0d1b91 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abQuake's host runs a frame only once at least 1/72 second has passed since the last, so the game steps at most 72 times a second; each frame's time step (
host_frametime) is the real time since the last frame, clamped to 0.001–0.1 second, unlesshost_frameratefixes it, and a time demo skips the 1/72 wait.Codeid-Software/QuakeHow we know
503 realtime += time; ⋮ 505 if (!cls.timedemo && realtime - oldrealtime < 1.0/72.0) 506 return false; // framerate is too high ⋮ 508 host_frametime = realtime - oldrealtime; ⋮ 511 if (host_framerate.value > 0) 512 host_frametime = host_framerate.value; ⋮ 515 if (host_frametime > 0.1) 516 host_frametime = 0.1; 517 if (host_frametime < 0.001) 518 host_frametime = 0.001;
WinQuake/host.c lines 503–519 at bf4ac42 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Quake builds the wish velocity from the forward and side moves along the view's forward and right vectors (zero vertical for a walking player), caps its length at
sv_maxspeed(default 320), and on the ground runs friction and thenSV_Accelerate: if the speed along the wish direction is under the wish speed, it addssv_accelerate(default 10) × frame time × wish speed along that direction, no more than the shortfall.Codeid-Software/QuakeHow we know
167cvar_t sv_maxspeed = {"sv_maxspeed", "320", false, true}; 168cvar_t sv_accelerate = {"sv_accelerate", "10"};
WinQuake/sv_user.c lines 167–168 at bf4ac42 · upstream ↗ 342 wishvel[i] = forward[i]*fmove + right[i]*smove; ⋮ 344 if ( (int)sv_player->v.movetype != MOVETYPE_WALK) 345 wishvel[2] = cmd.upmove; 346 else 347 wishvel[2] = 0; ⋮ 350 wishspeed = VectorNormalize(wishdir); 351 if (wishspeed > sv_maxspeed.value) ⋮ 353 VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel); 354 wishspeed = sv_maxspeed.value; ⋮ 361 else if ( onground ) ⋮ 363 SV_UserFriction (); 364 SV_Accelerate ();
WinQuake/sv_user.c lines 341–365 at bf4ac42 · upstream ↗ 190void SV_Accelerate (void) ⋮ 195 currentspeed = DotProduct (velocity, wishdir); 196 addspeed = wishspeed - currentspeed; 197 if (addspeed <= 0) 198 return; 199 accelspeed = sv_accelerate.value*host_frametime*wishspeed; 200 if (accelspeed > addspeed) 201 accelspeed = addspeed; ⋮ 204 velocity[i] += accelspeed*wishdir[i];
WinQuake/sv_user.c lines 190–204 at bf4ac42 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^On the ground, Quake's
SV_UserFrictiontakes the horizontal speed and lowers it by frame time × friction × the larger of the speed andsv_stopspeed, to no less than 0, scaling all three velocity axes by the same factor;sv_frictiondefaults to 4 andsv_stopspeedto 100, and when a trace 16 units ahead along the motion finds no floor within 34 units below the feet, the friction is multiplied byedgefriction(default 2). A standing player is left alone.Codeid-Software/QuakeHow we know
42cvar_t sv_friction = {"sv_friction","4",false,true}; 43cvar_t sv_stopspeed = {"sv_stopspeed","100"};
WinQuake/sv_phys.c lines 42–43 at bf4ac42 · upstream ↗ 27cvar_t sv_edgefriction = {"edgefriction", "2"};WinQuake/sv_user.c line 27 at bf4ac42 · upstream ↗ 132 speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); 133 if (!speed) 134 return; ⋮ 136// if the leading edge is over a dropoff, increase friction 137 start[0] = stop[0] = origin[0] + vel[0]/speed*16; ⋮ 140 stop[2] = start[2] - 34; ⋮ 144 if (trace.fraction == 1.0) 145 friction = sv_friction.value*sv_edgefriction.value; 146 else 147 friction = sv_friction.value; ⋮ 150 control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed; 151 newspeed = speed - host_frametime*control*friction; ⋮
WinQuake/sv_user.c lines 132–159 at bf4ac42 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Computed from the friction and acceleration rules, not measured: because acceleration (10) outpaces friction (4), a Quake player on flat ground settles at exactly the wish speed, 320 units/s at a run; at the 72-frame rate a run from rest reaches it on frame 10 (0.14 s), and letting go from 320 comes to rest after 39 frames (0.54 s).Codeid-Software/Quake
How we know
150 control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed; 151 newspeed = speed - host_frametime*control*friction;
WinQuake/sv_user.c lines 150–151 at bf4ac42 · upstream ↗ 195 currentspeed = DotProduct (velocity, wishdir); 196 addspeed = wishspeed - currentspeed; ⋮ 199 accelspeed = sv_accelerate.value*host_frametime*wishspeed; 200 if (accelspeed > addspeed) 201 accelspeed = addspeed;
WinQuake/sv_user.c lines 195–201 at bf4ac42 · upstream ↗ 42cvar_t sv_friction = {"sv_friction","4",false,true}; 43cvar_t sv_stopspeed = {"sv_stopspeed","100"};
WinQuake/sv_phys.c lines 42–43 at bf4ac42 · upstream ↗ 167cvar_t sv_maxspeed = {"sv_maxspeed", "320", false, true}; 168cvar_t sv_accelerate = {"sv_accelerate", "10"};
WinQuake/sv_user.c lines 167–168 at bf4ac42 · upstream ↗ 505 if (!cls.timedemo && realtime - oldrealtime < 1.0/72.0)WinQuake/host.c line 505 at bf4ac42 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^In the air, Quake's
SV_AirAcceleratetakes the wish velocity's direction and length, caps that length at 30, and adds speed along the wish direction only while the velocity's component along it is under 30: the push issv_accelerate× the uncapped wish speed (up to 320) × frame time, cut to the shortfall below 30. There is no friction in the air.Codeid-Software/QuakeHow we know
207void SV_AirAccelerate (vec3_t wishveloc) ⋮ 212 wishspd = VectorNormalize (wishveloc); 213 if (wishspd > 30) 214 wishspd = 30; 215 currentspeed = DotProduct (velocity, wishveloc); 216 addspeed = wishspd - currentspeed; 217 if (addspeed <= 0) 218 return; 219// accelspeed = sv_accelerate.value * host_frametime; 220 accelspeed = sv_accelerate.value*wishspeed * host_frametime; 221 if (accelspeed > addspeed) 222 accelspeed = addspeed; ⋮
WinQuake/sv_user.c lines 207–225 at bf4ac42 · upstream ↗ 361 else if ( onground ) ⋮ 363 SV_UserFriction (); 364 SV_Accelerate (); ⋮ 366 else 367 { // not on ground, so little effect on velocity 368 SV_AirAccelerate (wishvel);
WinQuake/sv_user.c lines 361–368 at bf4ac42 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Computed from
SV_AirAccelerate, not measured: with a wish speed of 320 the push can reach 10 × 320 × frame time (44.4 at 1/72 s, more at longer frames), so a frame lifts the velocity's component c along the wish direction to 30 whenever c is between −14.4 and 30, and the new speed is √(v² + 900 − c²) for speed v. That is largest with the wish at 90° to the motion: each frame then adds √(v² + 900) − v, 1.40 units/s at 320 and 0.90 at 500, and turns the motion by atan(30/v), 5.4° per frame at 320. At 72 frames a second that is about 101 units/s gained per second at 320.Codeid-Software/QuakeHow we know
212 wishspd = VectorNormalize (wishveloc); 213 if (wishspd > 30) 214 wishspd = 30; 215 currentspeed = DotProduct (velocity, wishveloc); 216 addspeed = wishspd - currentspeed; ⋮ 220 accelspeed = sv_accelerate.value*wishspeed * host_frametime; 221 if (accelspeed > addspeed) 222 accelspeed = addspeed;
WinQuake/sv_user.c lines 212–224 at bf4ac42 · upstream ↗ 168cvar_t sv_accelerate = {"sv_accelerate", "10"};WinQuake/sv_user.c line 168 at bf4ac42 · upstream ↗ 505 if (!cls.timedemo && realtime - oldrealtime < 1.0/72.0)WinQuake/host.c line 505 at bf4ac42 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abQuake's gravity is the cvar
sv_gravity, defaulting to 800, and each level's QuakeCworldspawnsets it to 800 again, or to 100 on E1M8; every frame a walking player is not in water (water level 2 or more) or a water jump,SV_AddGravitytakes the entity's gravity field (1 when unset) ×sv_gravity× frame time off its vertical velocity, on the ground as well as in the air.Codeid-Software/Quake · id-Software/Quake-ToolsHow we know
44cvar_t sv_gravity = {"sv_gravity","800",false,true};WinQuake/sv_phys.c line 44 at bf4ac42 · upstream ↗ 196 if (self.model == "maps/e1m8.bsp") 197 cvar_set ("sv_gravity", "100"); 198 else 199 cvar_set ("sv_gravity", "800");
qcc/v101qc/world.qc lines 196–199 at c0d1b91 · upstream ↗ 381 eval_t *val; ⋮ 383 val = GetEdictFieldValue(ent, "gravity"); 384 if (val && val->_float) 385 ent_gravity = val->_float; 386 else 387 ent_gravity = 1.0; ⋮ 389 ent->v.velocity[2] -= ent_gravity * sv_gravity.value * host_frametime;
WinQuake/sv_phys.c lines 381–389 at bf4ac42 · upstream ↗ 1089 if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) ) 1090 SV_AddGravity (ent);
WinQuake/sv_phys.c lines 1089–1090 at bf4ac42 · upstream ↗ 858 return ent->v.waterlevel > 1;WinQuake/sv_phys.c line 858 at bf4ac42 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Because Quake runs friction in
SV_ClientThinkbefore the QuakeC jump inPlayerPreThink, and the on-ground flag is set by the walking move when it ends on a floor, a jump can come no sooner than the frame after landing, and that frame takes ground friction first: computed, a hop loses 4/72 of its horizontal speed (5.6%) at 72 frames a second above 100 units/s, or 8/72 (11.1%) when no floor lies within 34 units below a point 16 units ahead.Codeid-Software/Quake · id-Software/Quake-ToolsHow we know
612 SV_RunClients (); ⋮ 617 SV_Physics ();
WinQuake/host.c lines 612–617 at bf4ac42 · upstream ↗ 387 onground = (int)sv_player->v.flags & FL_ONGROUND;WinQuake/sv_user.c line 387 at bf4ac42 · upstream ↗ 361 else if ( onground ) ⋮ 363 SV_UserFriction (); 364 SV_Accelerate ();
WinQuake/sv_user.c lines 361–364 at bf4ac42 · upstream ↗ 1069 PR_ExecuteProgram (pr_global_struct->PlayerPreThink);WinQuake/sv_phys.c line 1069 at bf4ac42 · upstream ↗ 970 oldonground = (int)ent->v.flags & FL_ONGROUND; 971 ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
WinQuake/sv_phys.c lines 970–971 at bf4ac42 · upstream ↗ 284 if (trace.ent->v.solid == SOLID_BSP) ⋮ 286 ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
WinQuake/sv_phys.c lines 284–287 at bf4ac42 · upstream ↗ 1033 if (downtrace.plane.normal[2] > 0.7) ⋮ 1035 if (ent->v.solid == SOLID_BSP) ⋮ 1037 ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
WinQuake/sv_phys.c lines 1033–1037 at bf4ac42 · upstream ↗ 798 if (!(self.flags & FL_ONGROUND)) 799 return;
qcc/v101qc/client.qc lines 798–799 at c0d1b91 · upstream ↗ 144 if (trace.fraction == 1.0) 145 friction = sv_friction.value*sv_edgefriction.value; ⋮ 151 newspeed = speed - host_frametime*control*friction;
WinQuake/sv_user.c lines 144–151 at bf4ac42 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^QuakeWorld's
pmove.cchecks the jump before friction:PlayerMovecallsJumpButton, which clearsonground, and thenPM_Friction, whose ground friction runs only whileongroundis set, so a QuakeWorld hop on its first ground frame loses no speed to friction, where WinQuake's takes one frame of it (quake/hop-friction). Both still need the button released between jumps, and both add 270.Codeid-Software/QuakeHow we know
889 if (pmove.cmd.buttons & BUTTON_JUMP) 890 JumpButton (); 891 else 892 pmove.oldbuttons &= ~BUTTON_JUMP; ⋮ 894 PM_Friction ();
QW/client/pmove.c lines 889–894 at bf4ac42 · upstream ↗ 674 if (onground == -1) 675 return; // in air, so no effect ⋮ 677 if ( pmove.oldbuttons & BUTTON_JUMP ) 678 return; // don't pogo stick ⋮ 680 onground = -1; 681 pmove.velocity[2] += 270;
QW/client/pmove.c lines 674–681 at bf4ac42 · upstream ↗ 366 else if (onground != -1) // apply ground friction ⋮ 368 control = speed < movevars.stopspeed ? movevars.stopspeed : speed; 369 drop += control*friction*frametime;
QW/client/pmove.c lines 366–369 at bf4ac42 · upstream ↗ 1460 PlayerMove ();QW/server/sv_user.c line 1460 at bf4ac42 · upstream ↗ 361 else if ( onground ) ⋮ 363 SV_UserFriction (); 364 SV_Accelerate ();
WinQuake/sv_user.c lines 361–364 at bf4ac42 · upstream ↗ 1069 PR_ExecuteProgram (pr_global_struct->PlayerPreThink);WinQuake/sv_phys.c line 1069 at bf4ac42 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
Sources
Code
- Software, id. 2012. “Quake: the engine source release (id-Software/Quake: WinQuake and QuakeWorld).” Commit bf4ac42. id-Software/Quake @ bf4ac42 · licence, all uses
- Software, id. 2012. “Quake: the QuakeC source and compiler (id-Software/Quake-Tools, qcc/v101qc).” Commit c0d1b91. id-Software/Quake-Tools @ c0d1b91 · licence, all uses