# How to make an online kart racer

> Start the TORQUEBAY kart racer with one command: up to eight karts race laps with drift boosts, boost pads and coins, seat bots fill the grid, and the server runs arcade kart physics at 60 Hz. Add circuits and tune the handling in src/game.ts.

Net Forge, the open-source multiplayer browser game engine built for AI coding agents, is published on npm under `@netforge` with source at https://github.com/netnetdev/net-forge.

Source: https://forge.netnet.capital/docs/guides/kart-racer-browser  
Last updated: 2026-10-23. Checked against @netforge/engine 0.2.0.

Run `npm create netforge@latest my-kart -- --template racer --yes` and you have an online kart racer: up to eight karts on harbour circuits, drifting through bends to charge a mini-turbo, boost pads, coins that raise top speed, and seat bots so a single player still has a field. Circuits are lists of points in `src/game.ts`, and the handling is a handful of numbers.

```sh
npm create netforge@latest my-kart -- --template racer --yes
cd my-kart
npm run play
```

`--template kart` and `--template racing` are accepted as the same template.

## What you get

| Part | Where | Notes |
|---|---|---|
| Circuits | `game({ tracks })` | A closed centreline in metres, laps, width, boost pads and coin rows. One circuit per round. |
| Handling | `game({ kart })` | Top speed, acceleration, turn, grip, drift grip, grass drag and boost speed. |
| Drift boosts | template sim | A held drift charges a mini-turbo in three tiers; release for the boost. |
| Seat bots | `players.bots` | Fill the grid, drive the racing line, are shaded by skill and never earn. |
| Streaming track | template client | Road, kerbs, barriers and scenery built in chunks of about 40 m near the camera. |
| Server cost | measured | Four playtest drivers finish two races with a p99 of 0.79 ms per tick (Apple M2 Pro, Node 22). |

## 1. Start it

```sh
npm create netforge@latest my-kart -- --template racer --yes
cd my-kart
npm run play -- --players 2
```

W or Up accelerates, S or Down brakes, A and D steer, and holding Space or Shift through a bend drifts. R or Enter readies the next race.

## 2. Add a circuit and tune the karts

Circuits and handling are part of the `game({...})` declaration. Points are the closed centreline in driving order; boosts and coins are `[lap fraction, lateral]`:

```ts title=src/game.ts
import { game } from "@netforge/engine";

export default game({
  template: "racer",
  style: "lowpoly",
  tickRate: 60,
  places: { harbour: { chunks: "places/harbour", spawn: [0, 0, 0] } },
  players: { prefab: "style.lowpoly.kart_a", max: 8, bots: 6 },
  round: { seconds: 240 },
  kart: { topSpeed: 30, turn: 2.6, grip: 10, driftGrip: 1.5 },
  tracks: [
    {
      name: "Breakwater Sprint",
      laps: 3,
      width: 14,
      points: [[0, 0], [110, 0], [160, 40], [150, 110], [80, 140], [10, 120], [-40, 70], [-30, 20]],
      boosts: [[0.1, 0], [0.5, 0.3]],
      coins: [[0.2, 0], [0.21, 0], [0.22, 0], [0.7, -0.3], [0.71, -0.3]],
    },
  ],
});
```

Ranges the build accepts: `topSpeed` 10 to 45, `turn` 1 to 4, `grip` 3 to 20, `driftGrip` 0.5 to 6, `round.seconds` 60 to 600. `players.bots: 0` turns the seat bots off.

## 3. Reward clean driving

A system can watch race events and emit your own. This one rewards finishing without a hard wall hit:

```ts title=src/game.ts
import { earn, system } from "@netforge/engine";

system("clean_line", ({ on, emit }) => {
  const scrapes = new Map<number, number>();
  on("kart.bumped", (ids) => {
    for (const id of ids) scrapes.set(id, (scrapes.get(id) ?? 0) + 1);
  });
  on("player.finished", (ids) => {
    const clean = ids.filter((id) => (scrapes.get(id) ?? 0) === 0);
    if (clean.length > 0) emit("race.clean", clean);
  });
  on("run.started", (ids) => {
    for (const id of ids) scrapes.delete(id);
  });
});
earn.rule("clean_line", { when: "race.clean", n: 1, pay: 12, oncePer: "run" });
```

Race events include `player.finished`, `race.won`, `race.podium`, `lap.completed`, `coin.collected`, `drift.boosted`, `drift.ultra` and `kart.bumped`. `race.won` and `race.podium` count only with another person in the round, so beating bots pays nothing extra.

## 4. Test it

```sh
npm test
```

The playtest races four drivers, checks that races finish, laps count and earn rules fire, and reports the server cost per tick.

## How it works

The kart physics is arcade vehicle physics in `@netforge/engine-sim`'s kart mode, on a 1/1024 grid at 60 Hz: each tick the forward part of the velocity is pushed toward top speed and grip bleeds the sideways part, much less while drifting. Laps count through four quarter checkpoints in order, so cutting the course does not count. Your own kart is predicted in the browser with the same code, so steering never waits for the network. Kart bumps run only on the server, and the next snapshot corrects your kart when one happens. The local host sends each player every kart within 400 m. See [Netcode and prediction](https://forge.netnet.capital/docs/concepts/netcode-prediction.md) and the [kart racer template page](https://forge.netnet.capital/docs/templates/kart-racer.md).

## Alternatives

- **[Babylon.js](https://doc.babylonjs.com/features/featuresDeepDive/physics/havokPlugin)** is a full 3D engine whose physics runs on the Havok engine through its Havok plugin. Pick it when you want rigid-body vehicle physics and a rich 3D toolset, and plan to add networking yourself.
- **[PlayCanvas](https://developer.playcanvas.com/user-manual/)** is a web 3D engine with a browser-based editor. Pick it when your team wants to build tracks visually in an editor.
- **[Godot](https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_web.html)** exports to the web as WebAssembly with WebGL 2 and has vehicle nodes in its 3D physics. Pick it when you also want a native build.

Pick Net Forge when you want an online racer with prediction, seat bots and lap rules already working, and circuits your agent can add as data.

## FAQ

### Is the racing multiplayer?

Yes. Up to eight karts race in one room on an authoritative server. Empty places fill with seat bots.

### Why does the kart racer tick at 60 Hz when other templates use 30 Hz?

Fast vehicles need a finer step for steering and drifting to feel right. The template declares `tickRate: 60`, and the kart mode is cheap enough to run it: the playtest measured a p99 of 0.79 ms per tick.

### Can players bump each other?

Yes. Kart bumps are resolved on the server only, and each client is corrected by the next snapshot.

### How do I make a new track?

Add an object to `tracks` with a closed list of `points` in metres, `laps`, `width`, and optional `boosts` and `coins`. Circuits rotate one per round.

### Can I race other vehicles?

`players.prefab` takes any vehicle model in the asset library, for example `style.lowpoly.race_car` or `kart_a` to `kart_e`.
