NET FORGE DOCS

How to make a multiplayer 3D platformer for the web

Last updated Checked against @netforge/engine 0.2.0View as Markdown

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

Run npm create netforge@latest my-climb -- --template platformer --yes and you have a multiplayer platformer race: up to eight climbers run, jump, dash, ride moving platforms and bounce off springs to reach the flag first, with seat bots so one person still has a race. The jump feel is a handful of numbers in src/game.ts, and a route planner proves every course can be finished.

npm create netforge@latest my-climb -- --template platformer --yes
cd my-climb
npm run play

What you get

PartWhereNotes
Jump feelgame({ motor })Run speed, jump height, time to apex, fall gravity, jump cut, coyote time (0.1 s), jump buffer (0.13 s), air jumps, dash speed.
Coursesgame({ levels })Platforms, moving platforms, springs, thorns, coins and checkpoints. One course per round.
Race roundsengine RaceRunnerCountdown, race, results, next course.
Seat botsplayers.botsRun routes found by the route planner; never earn.
Softlock checkroute plannerThe playtest fails a course with no route to the flag.
Server costmeasuredFour playtest climbers finish seven rounds with a p99 of 0.033 ms per tick (Apple M2 Pro, Node 22).

1. Start it

npm create netforge@latest my-climb -- --template platformer --yes
cd my-climb
npm run play -- --players 2

2. Tune the feel

The movement numbers sit in the motor field of game({...}). Change a few at a time:

src/game.ts
export default game({
  template: "platformer",
  // ...the rest of the template's game({...}) stays as it is
  motor: { runSpeed: 8, jumpHeight: 3.6, airJumps: 1 },
});

Accepted ranges: runSpeed 3 to 14, jumpHeight 1 to 8, timeToApex 0.2 to 0.7, fallGravity 1 to 3, jumpCut 0.2 to 1, coyoteSeconds and bufferSeconds 0 to 0.25, airJumps 0 to 2, dashSpeed 6 to 30. The route planner re-checks every course with the new numbers, so a jump made too low to clear a gap fails the playtest instead of shipping.

3. Reward a clean climb

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

system("clean_climb", ({ on, emit }) => {
  const falls = new Map<number, number>();
  on("player.fell", (ids) => {
    for (const id of ids) falls.set(id, (falls.get(id) ?? 0) + 1);
  });
  on("player.finished", (ids) => {
    const clean = ids.filter((id) => !falls.get(id));
    if (clean.length) emit("climb.clean", clean);
  });
  on("run.started", (ids) => {
    for (const id of ids) falls.delete(id);
  });
});
earn.rule("speedy", { when: "player.finished", n: 1, pay: 6, oncePer: "run" });

Race events include player.finished, race.won, race.podium, coin.collected, checkpoint.reached, player.fell and spring.bounced. Winning against a field of bots pays nothing extra.

4. Test it

npm test

The playtest first asks the planner for a route to the flag on every course, then races four climbers through the rounds and checks that they finish and that earn rules fire.

How it works

The platform sim is pure TypeScript: swept AABB movement on a 1/1024 m grid at 60 Hz in @netforge/engine-sim's platform mode. Because it is deterministic, the browser predicts your own climber and replays it bit for bit when a snapshot arrives. Climbers pass through each other and coins are per climber, so nothing another player does can make your prediction wrong, which is why the race feels local even online. See Netcode and prediction and the platformer template page.

Alternatives

  • Phaser is a free, open-source 2D framework with WebGL and Canvas rendering. Pick it for a single-player 2D platformer with tile maps and sprite art.
  • Godot has a scene editor and a character body for platformer movement, and exports to the web as WebAssembly with WebGL 2. Pick it for a level-editor-first workflow and a native build.

Pick Net Forge when you want an online race with prediction and seat bots, and courses that a planner verifies after every change.

FAQ

What is coyote time and does the template have it?

Coyote time lets a jump still work for a moment after you run off a ledge. The template has 0.1 s of it, plus a 0.13 s jump buffer, variable jump height and a lighter apex while Jump is held.

Can players knock each other off?

No. Climbers are ghosts to each other, which keeps every player's movement predicted exactly.

How does the game know a course can be finished?

A route planner searches each course with the current movement numbers. If it finds no route to the flag, the playtest fails, so a broken course never reaches players.

Is the platformer 3D?

The courses are 3D scenes in every art style, played from a side view.

How many players can race?

Up to eight per room, with seat bots filling empty places.