Say hello
3D

Lapping Spa in the browser

How the RB18 in my footer stays on the road, picks its speed and gets filmed like a broadcast.

Author
Avinash Kamadri
Published
Reading time
3 min

Draft: written from the code in this repo. Rewrite it in your own voice before publishing.

At the bottom of my homepage a Red Bull RB18 laps Spa-Francorchamps, cut together like a TV broadcast. It’s plain three.js in one HTML file, no game engine. Here’s how the pieces fit.

The track and the line

The circuit is a glTF model, and the racing line comes from the TUMFTM racetrack-database, which publishes centerlines for real circuits. I turn those points into a closed Catmull-Rom curve and resample it every metre, so every lookup after that is just an array index: position, direction and speed at metre s.

const curve = new THREE.CatmullRomCurve3(points, true, "centripetal");
const N = Math.round(curve.getLength());
for (let i = 0; i < N; i++) pos.push(curve.getPointAt(i / N));

Keeping the car on the road

A centerline alone floats the car above crests and sinks it into dips. So for every metre I cast a ray straight down where the left and right wheels would be, 0.8 m either side of the line, and record where it hits the asphalt.

Casting thousands of rays at a detailed track mesh is slow, so the road meshes get a bounding-volume tree from three-mesh-bvh first. With that, the whole lap’s ground samples take a moment at load time.

Pitch and roll

Each frame I look up the ground under the front and rear axles (1.8 m ahead and behind) and under each side. Front minus rear gives the car’s forward direction, left minus right gives its sideways tilt, and a cross product gives “up”. That basis becomes the car’s rotation, so it climbs Eau Rouge nose-up and leans on the banking.

Picking a speed

There’s no physics engine. Instead the car gets a speed for every metre of the lap, worked out in three steps:

  1. Corner limit. Curvature comes from how much the heading changes over ±12 m. The fastest you can take a curve is roughly sqrt(grip / curvature), capped at 91 m/s.
  2. Acceleration. A forward pass stops the speed rising faster than the car could accelerate, with less push as it gets quicker.
  3. Braking. A backward pass makes it slow down before corners instead of arriving too fast.

Running both passes twice settles the loop where the end of the lap meets the start. The readout in the corner just shows that number in km/h.

Wheels that turn

The car model stores all four tyres in one mesh, which means they can only move together. At load time I split the triangles by which corner of the car they sit in, then give each wheel two pivots: an outer one that steers (front wheels only) and an inner one that spins.

Real wheel speed at 300 km/h is far faster than 60 frames a second can show; it strobes and looks like it’s rolling backwards. So the visible spin is capped.

The broadcast director

The camera work is a list of shots keyed by distance around the lap. Each one is a type:

For trackside shots I give the apex and which side to stand on, and the code places the camera from the track geometry. The zoom keeps the car about the same size on screen as it approaches, the way a real camera operator would. A couple of shots also slow time down, for Raidillon and the Bus Stop.

Keeping it light

Type /f1 on the homepage to jump straight to it.