multiplayer
Skill4 starsChanged 7 months agoMITView on GitHub
Adds optional multiplayer to any generated Phaser 3 game. Supports three tiers — local (two gamepads/keyboards, no server), LAN (Colyseus authoritative server, same network), and online (Colyseus + TURN/STUN relay). Optional React/Redux lobby and PeerJS voice/video chat. Trigger only when the user explicitly requests multiplayer ("make it 2-player", "add online co-op", "--multiplayer" flag).
- Reads credentials
- Installs packages
5 other projects use a version of this.
Adopted by
Most recently taken up by
- sid-lakhani/agents-configtheir own version, first seen Oct 20, 2026
- xenitV1/Antigravity-Workflowstheir own version, first seen Oct 8, 2026
- VoDaiLocz/kilo-kit-mcptheir own version, first seen Aug 21, 2026
- SKB3002/agenthubtheir own version, first seen Apr 20, 2026
- bcastelino/agent-skills-kittheir own version, first seen Feb 28, 2026
Other versions5 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 5
- xenitV1/Antigravity-Workflows★ 130same name
- VoDaiLocz/kilo-kit-mcp★ 28same name
- SKB3002/agenthub★ 4same name
- bcastelino/agent-skills-kit★ 2same name
- sid-lakhani/agents-config★ 2same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository22 other files it gives its agents.
- action-platformerSkill
- audio-composerSkill
- beat-em-upSkill
- bg-artistSkill
- codesmithSkill
- game-designerSkill
- gameforgeSkill
- gap-checkerSkill
- iso-city-builderSkill
- iso-living-worldSkill
- manifest-auditorSkill
- mobile-compat-checkerSkill
- palette-enforcerSkill
- parallel-qa-orchestraSkill
- physics-debug-validatorSkill
- pixel-action-adventureSkill
- playtesterSkill
- refinerSkill
- rpg-overworldSkill
- sprite-artistSkill
- tile-artistSkill
- world-architectSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 18
Do not activate automatically for single-player games.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Multiplayer — Optional Networked Play for Phaser 3 Games
Adds network play to any game already generated by the gameforge pipeline. The skill is additive and optional — it patches existing files minimally and creates new files under server/ and src/net/. It never rewrites src/scenes/Game.js from scratch; it injects hooks.
When to use
Trigger on any of:
- Explicit multiplayer request in the prompt ("2-player", "co-op", "online", "multiplayer")
--multiplayerCLI flag passed togameforge generate--local,--lan, or--onlinetier flags- "add a lobby", "add voice chat" (implies multiplayer)
Do not activate automatically for single-player games. Always confirm tier with the user if ambiguous (local vs LAN vs online costs differ significantly in deployment complexity).
Three tiers
Tier 1 — Local only (no server)
Two players on the same machine or two gamepads/keyboards. No network code, no Colyseus. Player 2 input uses WASD (if P1 uses arrows) or gamepad index 1. State is shared in the same Phaser scene. Fastest to implement; zero infrastructure.
Trigger: --local or "same screen" or "splitscreen" in the prompt.
What gets added:
- A second player sprite spawned from the same entity pool as P1
- P2 input wiring (WASD or gamepad 1)
- Optional split-screen camera (two Phaser cameras, each following one player)
- Shared HUD showing both HP/score
Tier 2 — LAN (Colyseus, same network)
Authoritative Colyseus server running locally. Both clients connect over WebSocket to ws://localhost:2567. No TURN/STUN needed — NAT traversal assumed handled by router or VPN. Suitable for game jams, LAN parties.
Trigger: --lan or "local network" or "same WiFi" in the prompt.
What gets added:
server/directory with Colyseus server (TypeScript, compiled toserver/dist/)src/net/ColyseusClient.js— connects, joins room, forwards input, applies server statesrc/net/RemotePlayer.js— interpolated sprite for each networked peerpatch_game.mjsinjects net hooks intosrc/scenes/Game.js
Tier 3 — Online (Colyseus + TURN/STUN)
Full internet play. Colyseus server hosted on a public URL. ICE servers (TURN/STUN) configured for WebRTC signaling via PeerJS voice/video. Requires a .env with VITE_COLYSEUS_URL and optionally VITE_PEER_HOST.
Trigger: --online or "internet", "play with friends online" in the prompt.
What gets added:
- Everything in Tier 2
.env.examplewithVITE_COLYSEUS_URL,VITE_PEER_HOST,VITE_PEER_PORT,VITE_PEER_PATH- ICE server configuration in
ColyseusClient.js - Optionally:
src/net/VoiceChat.js(PeerJS, triggered by--voice) - Optionally:
lobby/React app (Vite + React + Redux, triggered by--lobby)
Player session schema
Each connected player carries this state on the server (authoritative) and mirrored to all clients:
// src/schemas/GameState.ts (server-side)
export class Player extends Schema {
@type('string') sessionId: string = '';
@type('string') name: string = 'Player';
@type('number') x: number = 0;
@type('number') y: number = 0;
@type('number') hp: number = 3;
@type('number') score: number = 0;
@type('boolean') left: boolean = false;
@type('boolean') right: boolean = false;
@type('boolean') up: boolean = false;
@type('boolean') down: boolean = false;
@type('boolean') action: boolean = false;
}input fields (left, right, up, down, action) are written by the client and consumed by the server simulation loop. Position, HP, and score are written by the server and read by clients.
How the host agent should invoke the scripts
Step 1 — init server (Tier 2 or 3 only)
node skills/multiplayer/scripts/init_server.mjs <project-dir> [--port 2567] [--voice] [--lobby]This reads game-state.json, creates server/ and src/net/, and optionally lobby/.
Options:
--port N— Colyseus WebSocket port (default: 2567)--voice— also createsrc/net/VoiceChat.js(PeerJS)--lobby— also scaffoldlobby/React app
Step 2 — patch Game.js
node skills/multiplayer/scripts/patch_game.mjs <project-dir>Reads existing src/scenes/Game.js, injects ColyseusClient and RemotePlayer hooks, writes the patched file back. Genre-aware (platformer vs top-down vs shooter).
Step 3 — start server + client
# Terminal 1 — Colyseus server
cd <project-dir>/server && npm install && npm run dev
# Terminal 2 — Phaser client
cd <project-dir> && npm run devFor local-only tier, skip Step 1 and Step 3 server start. patch_game.mjs --local adds P2 input only.
File structure created
<project>/
├── server/ # Colyseus authoritative server (Tier 2+)
│ ├── package.json # colyseus, @colyseus/monitor, typescript
│ ├── tsconfig.json
│ └── src/
│ ├── index.ts # HTTP + WS bootstrap, /colyseus monitor
│ ├── rooms/
│ │ └── GameRoom.ts # Room<GameState>, 20 Hz sim loop
│ └── schemas/
│ └── GameState.ts # MapSchema<Player> + Player schema
├── src/
│ └── net/ # Phaser client networking
│ ├── ColyseusClient.js # Colyseus.Client, room join, state sync
│ ├── RemotePlayer.js # Phaser sprite + linear interpolation
│ └── VoiceChat.js # PeerJS voice/video (--voice only)
└── lobby/ # React matchmaking UI (--lobby only)
├── package.json # react, react-redux, @reduxjs/toolkit, vite
├── vite.config.ts
└── src/
├── main.tsx
├── store.ts # Redux store
├── slices/
│ └── lobbySlice.ts # rooms list, player name, join status
└── components/
├── App.tsx
├── RoomList.tsx
└── PlayerSetup.tsxHow Game.js is patched
patch_game.mjs performs three minimal injections into the existing Game.js:
Injection 1 — imports (top of file)
import ColyseusClient from '../net/ColyseusClient.js';
import RemotePlayer from '../net/RemotePlayer.js';Injection 2 — end of create()
Inserted just before this.events.emit('scene-ready'):
// --- multiplayer net ---
this._net = new ColyseusClient(this);
this._remotePlayers = new Map();
this._net.onStateChange((state) => this._syncFromServer(state));
// --- end multiplayer net ---Injection 3 — end of update()
Inserted at the end of the update() body:
if (this._net) this._net.sendInput(this._collectInput());Injection 4 — new methods appended to class body
_collectInput() {
return {
left: this.cursors.left.isDown || this.keys?.A?.isDown,
right: this.cursors.right.isDown || this.keys?.D?.isDown,
up: this.cursors.up.isDown || this.keys?.W?.isDown,
down: this.cursors.down.isDown || this.keys?.S?.isDown,
action: this.keys?.SPACE?.isDown || false,
};
}
_syncFromServer(serverState) {
const myId = this._net.sessionId;
serverState.players.forEach((player, sessionId) => {
if (sessionId === myId) return; // local player moved by physics
if (!this._remotePlayers.has(sessionId)) {
const rp = new RemotePlayer(this, player.x, player.y, player.name);
this._remotePlayers.set(sessionId, rp);
} else {
this._remotePlayers.get(sessionId).syncFrom(player);
}
});
// destroy departed players
this._remotePlayers.forEach((rp, id) => {
if (!serverState.players.has(id)) { rp.destroy(); this._remotePlayers.delete(id); }
});
}The patch preserves all existing logic; it never removes lines.
PeerJS voice/video layer (--voice)
src/net/VoiceChat.js provides optional peer-to-peer voice and video chat between players. It uses PeerJS as the signaling and connection layer on top of WebRTC.
Activation: pass --voice to init_server.mjs. The Phaser scene calls VoiceChat.init(sessionId, peersArray) after the Colyseus room join is confirmed.
The Colyseus room broadcasts a peer-joined message when a new player arrives. Existing players call voiceChat.call(newPeerId). The new player auto-answers all incoming calls. Video elements are rendered via this.add.dom() in a Phaser scene or mounted to a separate <div> overlay above the canvas.
See references/peerjs-video.md for implementation details.
React lobby (--lobby)
lobby/ is a standalone Vite React app that handles pre-game matchmaking:
- Player sets their name.
- App fetches room list from
http://localhost:2567/matchmake/lobby(Colyseus HTTP API). - Player creates or joins a room.
- On join success, the lobby redirects to the Phaser game URL with
?room=<roomId>&session=<sessionId>query params. ColyseusClient.jsreads those params to rejoin the already-assigned room.
Redux store shape:
{ lobby: { playerName, rooms, status, error, roomId, sessionId } }See references/colyseus-schema.md for the Colyseus HTTP matchmaking API details.
Constraints
- Max 4 players per room (set in
GameRoom.tsmaxClients = 4). - Tick rate: 20 Hz —
this.clock.setInterval(this._simulate.bind(this), 50)inGameRoom.ts. - State sync: every 50 ms — Colyseus default patch interval; do not lower below 50ms for browser games.
- Input authority: clients send input; server computes position. Clients show server position for remote players (no client-side prediction for simplicity — add it in refiner if needed).
- Local player: client still moves the local player sprite via Phaser physics for zero-latency feel, but server position reconciles every sync (smooth toward server pos if delta > 8px).
- No split-screen in Tier 2+: with network players, each client renders only their own player via Phaser physics and all remote players via RemotePlayer sprites.
- ESM throughout:
ColyseusClient.js,RemotePlayer.js,VoiceChat.jsare all ES modules. - TypeScript on server only: the Phaser client remains vanilla JS (matching codesmith convention). Server code is TypeScript compiled with
tsc.
References
references/colyseus-schema.md— Colyseus Schema decorators, MapSchema, state listenersreferences/peerjs-video.md— PeerJS API, getUserMedia, video in Phaser
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

