How to make a multiplayer idle game in the browser
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-idle -- --template idle --yes and you have a shared idle game: up to eight players each run a works on one salt flat (pumps draw brine, pans boil it into salt, kilns cut crystals) and everyone ships crystals to one contract. The production chain, costs and growth curves are declarations in src/game.ts.
npm create netforge@latest my-idle -- --template idle --yes
cd my-idle
npm run playOne design choice is different from most idle games, on purpose: there is no offline progress. Production happens only on the room's clock while you are in the room, and a works stalls two minutes after you last tend it. That is what lets a multiplayer idle game stay fair and keeps rewards from being farmed by an open tab.
What you get
| Part | Where | Notes |
|---|---|---|
| Production chain | plant({...}) | 1 to 8 plants with at least one pump, one pan and one kiln. Each level costs growth times the last. |
| Shared goal | saltFlat({ contract }) | A barge takes crystals from everyone for one contract. |
| Shifts | saltFlat({ shift }) | A run is a shift of 600 to 3,600 seconds (15 minutes in the template). |
| Anti-farm rules | build checks | tend is at most 300 s; no earn rule may pay sooner than 180 s into a fresh works, which the playtest measures. |
| Players | room | Up to 8 per flat. |
| Playtest | npm test | Four players for four minutes, plus a full shift, run in 1.35 s in total (Apple M2 Pro, Node 22). The first payout comes 199 s into a fresh works. |
1. Start it
npm create netforge@latest my-idle -- --template idle --yes
cd my-idle
npm run play -- --players 22. Change the progression curve
import { plant } from "@netforge/engine";
plant("hand_pump", { model: "style.lowpoly.barrel", kind: "pump", cost: 15, growth: 1.15, rate: 3 });
plant("deep_bore", { model: "style.lowpoly.generator_large", kind: "pump", cost: 5000, growth: 1.2, rate: 200 });A lower growth keeps levels cheap for longer; a higher rate makes each level count more. Add each new plant's label to src/names.json under weapons. The bar shows plants in order, on keys 1 to 8.
3. Lengthen the shift and set a bigger contract
saltFlat({ shift: 1200, contract: { crystals: 120 }, ... });The template's saltFlat(...) has more fields (the flat, its layout and timings); change shift and contract in place rather than writing a new one.
4. Pay for real play, not for time
import { earn } from "@netforge/engine";
earn.rule("first_crate", { when: "salt.packed", n: 30, pay: 5, oncePer: "run" });Events are salt.packed, crystal.cut, crystal.shipped, plant.built, plant.upgraded and contract.filled. Every rule is once per run. If a change makes a rule pay sooner than 180 seconds into a fresh works, the playtest fails with the number and tells you to raise the rule's n or slow the curve.
How it works
The idle game is a rules genre: a deterministic module the room steps at 30 Hz, with integer state and no wall clock. Output is computed only on the room's ticks for players in the room, never from a client-reported number or elapsed real time. A player who leaves and comes back starts a fresh works from 20 kg of salt. Contract progress is world progress, so it never pays a player whose run started after the shift began. See Testing with playtest bots and the idle game template page.
Alternatives
- Phaser suits a single-player 2D incremental game in the browser, with offline progress computed on the client from saved timestamps.
- Colyseus gives you an authoritative room server with state sync if you want to write a shared idle economy's rules as server code.
Pick Net Forge when you want a shared idle game where everyone's progress is decided on the server and a bot playtest checks the pacing.
FAQ
Does the idle template have offline progress?
No, by design. Production runs only on the room's clock while you are in the room, and a works stops two minutes after its last tend. That keeps a shared game fair; for a single-player idle game with offline progress, a client-side approach fits better.
How many players share one game?
Up to eight per salt flat, each with their own works and one shared contract.
How long is a run?
A shift, 15 minutes in the template, adjustable from 10 to 60 minutes with saltFlat({ shift }).
Can clicking faster earn more?
Raking has a cooldown, so mashing adds nothing. The playtest simulates perfect play and pure raking to check that no rule pays too early.
What happens when I leave?
Your works stops. Rejoining starts a fresh works, so time away never accumulates.