Research & Reference
The Bike Doesn't Know It's a Bike
How a chassis, two sliding wheels, and one honest friction rule add up to a 2D dirt bike that wheelies, lands, and crashes on purpose.
A dirt bike that feels right is not a sprite dragged along a spline. It is a rigid body with no idea it is a bike — a chassis, two wheels, some springs, and a friction rule — that happens to wheelie, whip, and eat dirt because the forces point the right way. The Physics R&D department wrote the entire model down before a single line of engine code existed, which is the only sane order: you cannot tune what you have not first specified. What follows is that reference, made readable.
The game is one offline HTML page — nothing to install, no server, no assets streamed in. The engine tone is synthesised live through Web Audio, an oscillator whose pitch tracks the throttle so the bike growls when you pin it and settles when you lift. Every number here was chosen by an AI studio from a single prompt, then written up as a spec so the implementation had something to be correct against.
A chassis and two wheels that slide
Canvas coordinates run y-down, so gravity is +y and a positive chassis angle θ pitches the nose down. The bike is one rigid body — position, velocity, orientation, angular velocity — with two wheels bolted on as one-degree-of-freedom sliders. A wheel is not a free body in the world; it is a point constrained to travel along the chassis' local down-axis dn = (-sinθ, cosθ), riding a spring-damper:
F_susp = -k*(s - L0) - c*sDot // along dn; wheel gets +F, chassis gets -F at the mountThe reaction lands at the mount offset, so suspension force does double duty: it holds the bike up, and through r × F it pitches it. That single accounting choice — every force applied at its real contact point, torque falling out of the cross product — is what lets the rest of the model behave without special cases.
Traction is a spin problem, not a speed problem
The tempting shortcut is to make friction chase ground speed. The reference refuses. A real tyre grips against the difference between its contact-patch surface speed and the ground, and that surface speed comes from wheel spin φ̇. So the model tracks slip directly:
v_slip = vT - phiDot*r
F_fric = clamp(-kT * v_slip, -mu*N, +mu*N)Friction is a spring that opposes slip, capped by the traction budget μ·N. Drive torque spins the rear wheel up; that drives v_slip negative; friction turns positive and shoves the bike forward — until demand exceeds μ·N, at which point the tyre lets go and spins. Wheelspin, lockup, and clean drive are the same equation in three regimes, not three code paths. And because spin is a genuine state variable, the wheel keeps revving in the air and bites on touchdown exactly as a real one does.
Wheelies are not a special case either
You never write if throttle, rotate nose up. Two forces already do it. The drive torque on the rear wheel has an equal-and-opposite reaction on the chassis, -τ_drive, which pitches the nose up. And the forward tractive force acts at the rear contact, below the centre of mass, so r × F is a nose-up moment that unloads the front suspension. The front extends, its normal force drops, the wheel leaves the ground. This is the weight-shift model: the rider's Left/Right keys add a pure chassis torque that biases the transfer — weak on the ground, where contact forces fight it, and dominant in the air, where nothing does.
Air, landings, and eating dirt
When both wheels report zero contact, the bike is a free rigid body: gravity integrates the arc, and lean torque is the only pitch authority — enough, sized right, to carry a full 360 through a jump's airtime. Landing runs the springs backward. Suspension absorbs the closing speed as the soft stage; a deliberately stiff contact penalty, N_force = max(0, kN*pen - cN*vN), is the hard backstop for when it bottoms out. Impact speed sorts the outcome into cushioned, jolted, or wrecked. You crash when the rider body itself touches terrain, when a wheel contacts while the bike is inverted past θ_crash, or when you land faster than vCrash.
Why 240 Hz
The hard contact spring is stiff by design — the ground does not give — and stiff springs explode at large timesteps. The fix is sub-stepping: physics runs on a fixed 1/240 s accumulator, four sub-steps per 60 fps frame. The small step keeps the penalty force stable, and it stops a fast wheel from stepping clean over a sharp crest between updates. A wheel doing 2000 px/s covers only about 8 px per sub-step — less than its radius — so it physically cannot tunnel through a steep face. Where a level has near-vertical walls, you drop that section to 1/300 s rather than pile on sub-steps unbounded. The whole loop — reset forces, read input, suspension, contact and friction, drive, integrate the sliders, integrate the chassis, rest-clamp, crash-check — is nine ordered steps, and it fits in one page that runs with the lights off.