A minimal, fully-working starting point for building a real-time, .io-style movement game on Plot. The server handler owns an authoritative world of player positions; the browser client predicts its own movement and interpolates everyone else from server snapshots. It is intentionally lean — one circle per player, mouse-to-move, camera centred on you — so it is the smallest thing that demonstrates the prediction + interpolation pattern and nothing more. Clone it, run it, and grow it into your game.
Any top-down, real-time multiplayer game where many players move around a shared arena and the server is the source of truth: agar-style eaters, snake/slither clones, twin-stick shooters, last-one-standing arenas. Start here instead of wiring up netcode from scratch.
npm install
# Point at a Plot deployment. Either:
# a) use your hosted app key from the dashboard at https://plot.ws
export VITE_PLOT_APP_KEY=pl_pub_your_app_key
# b) or run Plot locally and point the client at it
export VITE_PLOT_API_URL=http://localhost:8787
npm run dev # http://localhost:5173Optional env vars (all have local-dev defaults):
| Variable | Default | Purpose |
|---|---|---|
VITE_PLOT_APP_KEY |
pl_pub_local_dev |
Your public app key from the Plot dashboard. |
VITE_PLOT_ROOM |
IO1 |
Room code players join — share it to play together. |
VITE_PLOT_API_URL |
(SDK default) | Override the Plot API endpoint (e.g. a local server). |
Open the page in two tabs (or share the URL) to see two players in the same room move in real time.
- The handler owns positions (
src/handler.ts). On join a player spawns at the origin; eachmovemessage integrates one step via the purestepMoveinsrc/logic.ts; on leave the player is removed. This same handler runs on the server and locally in the client for prediction. - The client predicts + interpolates (
src/main.ts). Itpredicts its ownpositions.<id>for instant, reconciled local movement, andinterpolatespositions.*so remote players render smoothly from buffered snapshots. One predictedmoveis sent per animation frame.
Because src/logic.ts is pure (no @plot/* imports), it is unit-tested in isolation (src/logic.test.ts) and reused verbatim on both sides — which is exactly what keeps predicted motion in step with the server.
- Add mass + eating to turn it into an agar.io clone (size drives speed, big absorbs small).
- Add a leaderboard with
ctx.leaderboard('...').submit(id, score)in the handler. - Add pellets/food, obstacles, or arena hazards in the handler's
onTick. - Add WASD / touch controls alongside the mouse vector.
- Add player names, colours, and a HUD over the canvas.
- Add hit detection using the server's
ctx.rewindTofor lag-compensated interactions.
@plot/client and @plot/handler are vendored under vendor/ and referenced as file: dependencies so this template runs offline. Once the SDKs are published, replace the two file: entries in package.json with:
npm i @plot/client @plot/handlernpm run build emits a static bundle in dist/ — deploy it to any static host (e.g. Cloudflare Pages); the Plot handler runs on your Plot deployment.
MIT — see LICENSE.
