# A realistic car, then an arcade car on top of it

> How the car in Threejs-Punk drives: raycast suspension on Rapier, a tire and drivetrain model, a bench that measures it, and the arcade layer that made it fun.

Canonical page: https://andersonmancini.dev/blog/vehicle-physics

Published: 2026-09-25 · 4 min read · Tags: game-dev, physics, three.js, rapier, racing

There are two ways to make a car for a racing game. You can simulate one, with suspension, tires and a drivetrain, and you get something that feels like a car and is hard to drive with a keyboard. Or you can fake it, with a body that slides toward where you point it, and you get something easy and weightless.

The car in [Threejs-Punk](https://andersonmancini.dev/blog/threejs-punk-racing-game.md) is both. There is a real simulation underneath, and an arcade layer on top of it, and the second one exists because of what the first one did to a keyboard.

## The simulation

The car is a single Rapier rigid body, 1,480 kg, with an explicit center of mass and inertia. It has no wheel colliders. Each wheel is a **ray** cast down into the world, and the rest is a model I wrote:

- **Suspension:** a ray per wheel against the static geometry, with a spring, a damper and an anti-roll bar. It yields the load on each wheel.
- **Tires:** normalized Pacejka-style curves with combined slip, a friction ellipse that shares grip between braking and cornering, and relaxation lengths so the car is stable at low speed.
- **Drivetrain:** a torque curve, a clutch, a dual-clutch style gearbox that matches revs and a limited-slip differential.
- **Brakes:** stick-slip friction, ABS per wheel and a handbrake on the rear axle.
- **Aids:** traction control, and a stability control that brakes the outer front wheel when the car rotates more than the steering asks for.

It runs at 120 Hz with four sub-steps for the tires, and the visuals interpolate between steps.

## Two bugs that taught me something

**The fake bump.** The damper needs the speed at which the wheel moves along the suspension. My first version used the chassis point's velocity along the chassis' *up* axis. When the car pitches, that axis tilts, and the forward speed leaks into it: about 0.5 m/s of fake bump at 250 km/h for each 0.4 degrees of pitch. The damper pumped the body under braking, the load on the rear wheels swung between 0.9 and 8.7 kN, and the car was nearly undrivable fast. The fix was to project the velocity on the **ground normal** instead.

**The crab.** At 250 km/h, two degrees of steering at the wheel ask for several g of lateral force. A held key gave two degrees, so both axles ran at the limit and the car walked sideways. The steering now limits the angle by speed, from 5.5 degrees in town to 0.8 from about 160 km/h.

## Measuring it

The physics runs headless, with the exact code of the game, in a bench that prints the numbers a magazine would:

| Test | Result |
|---|---|
| 0 to 100 km/h on a wet road | about 4.9 s |
| Top speed | about 317 km/h |
| 100 to 0 km/h with ABS | about 24.5 m |
| Skidpad at 80 km/h | 1.02 g |
| Step steer, overshoot and rise | 17.6% and 167 ms |

A bench changes how you tune. Instead of "it feels heavy", you have a number that moved.

## Why it still needed an arcade layer

The simulation was right, and it was not fun with a keyboard. Wet tires top out around 1 g, and the steering clamp that keeps the car stable leaves about 1.2 degrees of road wheel at 200 km/h. A held key turned the car 18, 15 and 11 degrees a second at 120, 150 and 200 km/h. A 90 degree corner at 150 km/h took more than four seconds.

So the arcade layer sits on top of the tires, applied at the center of mass:

- **Turn assist** sets a target yaw rate from the steering, `steer × min(v·tan δmax / L, lateralG·g / v)`, with a lateral g of 2.3, and a yaw torque chases it. A sideways push removes the lateral velocity, so the car goes where it points.
- **Drift** is a mode: above 43 km/h, with some steering, the tires' slip is reduced to 20%, a sideways push bends the path into an arc and a heading controller holds the drift angle. Full throttle keeps the speed, and lifting sheds about 20 km/h a second.
- **Brake assist** adds a deceleration on top of what the wet tires can give, because a pure tire model made a long, soft stop.
- **Nitro** is a push along the heading that fades near 340 km/h, from a tank that empties in about 4.5 seconds.

The simulation gives the car its weight and its behavior over bumps, and the arcade layer gives it its response. The arcade brakes and drag act at the center of mass, so they add no yaw. The only things that rotate the car on purpose are the turn assist and the drift controller.

## The takeaway

I do not think there is a "right" amount of simulation. There is the amount that survives contact with the input device. Build the realistic car anyway, because it gives you the weight and the feel over a curb, then measure it, and then add exactly as much arcade as the keyboard needs.

## Related projects

- [Threejs-Punk](https://andersonmancini.dev/projects/threejs-punk.md) — Fully featured Racing Game
