Easy Multiplayer
Author deterministic browser games as host-agnostic modules, then run them through rollback, schema-backed performance mode, and Trystero peer transport.
Get Started See Demosimport { defineGame } from './defineGame.js';
export default defineGame({
initialState: () => ({ ball: { x: 50, y: 30, vx: 1, vy: 1 } }),
sampleInput: () => ({ dy: (keys.ArrowDown ? 1 : 0) - (keys.ArrowUp ? 1 : 0) }),
step(state, ctx) {
state.ball.x += state.ball.vx;
state.ball.y += state.ball.vy;
},
draw(state, ctx) { /* render from state or a mirror */ }
});
Current Authoring Model
New games should start from defineGame: a plain module with initialState, deterministic step, optional draw, and optional sampleInput. The module owns gameplay only. The host supplies transport, clock, participants, rollback, and rendering environment.
For larger games, use V0.2/V0.3 performance mode. Canonical state is declared with schemaScalar, schemaRecord, and schemaTable; simulation writes through ctx.world() inside step(ctx); render code reads a RenderMirror. That schema-backed performance mode is the current scalable path.
Which Mode Should I Use?
| Use case | Recommended path |
|---|---|
| New small game, tutorial, or host integration | Start with defineGame. Keep step(state, ctx) deterministic and host-agnostic. |
| High-entity-count game, Pac-Man-scale port, worker rendering, or rollback-heavy simulation | Use V0.2/V0.3 performance mode: schema-backed state, ctx.world(), deterministic step(ctx), and mirror-backed rendering. |
| Easy/proxy mode object graphs | Future/deferred work. Do not choose easy/proxy mode as a current recommended path. |
Existing callback code using new EasyMultiplayer, defineInput, and on('tick') | Treat it as legacy compatibility. Do not teach new developers that path first. |
| Bus managers, scheduled buses, runtime adapters, and workers | Runtime infrastructure only. They are not a separate game-authoring API. |
Key Concepts
defineGame Modules
A game definition is pure data plus functions. It imports gameplay utilities, not networking or browser transport.
Schema-Backed Performance Mode
Declare canonical rows up front. The backend snapshots, hashes, patches, transfers, and mirrors plain records efficiently.
ctx.world()
The world facade exposes class-bound get, all, spawn, destroy, input, participants, tick, and dt.
Deterministic step(ctx)
Simulation reads routed inputs and schema rows only. No wall-clock time, DOM, renderer state, or ambient randomness.
Mirror-Backed Rendering
The renderer consumes RenderMirror snapshot and patch data. Render objects never become authoritative simulation state.
Trystero Transport
Standalone browser demos use TrysteroTransport for peer-to-peer rooms; tests inject deterministic transports.
Current Demos
- Pac-Man V0.2 local performance demo
- Pac-Man V0.2 Trystero demo
- Pac-Man V0.2/V0.3 world-facade Trystero demo - the release-facing tutorial path
- V0.2 game-authoring guide
- Performance-mode developer API guide
Advanced Diagnostics
The live V0.3 worker Pac-Man proof is available at https://libs.letsinspire.com/em-pacman-v03-worker-trystero/. Treat time-sliced and worker proof URLs as advanced diagnostics for runtime validation, not first-path getting-started material. Start tutorials from the world-facade demo above.
Deployment Note
Publish the refreshed public docs by deploying the contents of docs/ to https://libs.letsinspire.com/easy_multiplayer/docs/. After upload, smoke-check index.html, getting-started.html, examples.html, and the linked Pac-Man demo pages from that base URL.