From vibedgames
Adds real-time multiplayer to browser games using @vibedgames/multiplayer for shared state, player state, events, co-op/PvP. Supports React hooks, vanilla JS, Phaser, Three.js.
npx claudepluginhub kyh/vibedgamesThis skill uses the workspace's default tool permissions.
Add real-time multiplayer to any browser game with `@vibedgames/multiplayer`.
Adds real-time or turn-based multiplayer to existing Phaser 3 or Three.js browser games using PartyKit on Cloudflare Durable Objects. Scaffolds room-based server, NetworkManager client, EventBus events, GameState fields, and extends render_game_to_text().
Provides expertise in real-time multiplayer game networking: lag compensation, state synchronization, authoritative servers, rollback netcode, matchmaking, and anti-cheat.
Defines ownership, authority, replication, prediction, rollback, and reconciliation strategies for multiplayer systems. Useful for networked state sync with lag or authority issues.
Share bugs, ideas, or general feedback.
Add real-time multiplayer to any browser game with @vibedgames/multiplayer.
npm install @vibedgames/multiplayer
@vibedgames/multiplayer — framework-agnostic MultiplayerClient class (Phaser, Three.js, vanilla JS)@vibedgames/multiplayer/react — React hooks wrapping the clientThree types of state:
The host (first player) typically runs authoritative game logic.
import { useMultiplayerRoom, useMultiplayerState, usePlayerState, useIsHost } from "@vibedgames/multiplayer/react";
const room = useMultiplayerRoom({
host: "https://vibedgames-party.kyh.workers.dev",
party: "vg-server",
room: "my-game-room",
});
const [world, setWorld] = useMultiplayerState(room, { score: 0 });
const [me, setMe] = usePlayerState(room, { x: 0, y: 0 });
const isHost = useIsHost(room);
if (isHost) setWorld({ score: world.score + 10 });
const onPointerMove = (e: PointerEvent) => {
setMe({ x: e.clientX, y: e.clientY });
};
room.sendEvent("explosion", { x: 100, y: 200 });
// Receive via onEvent config:
const room = useMultiplayerRoom({
host, party, room,
onEvent: (event, payload, from) => { /* handle */ },
});
import { MultiplayerClient } from "@vibedgames/multiplayer";
const client = new MultiplayerClient({
host: "https://vibedgames-party.kyh.workers.dev",
party: "vg-server",
room: "my-game-room",
initialState: { phase: "playing" },
onEvent: (event, payload, from) => { /* handle */ },
});
// Subscribe to state changes
client.subscribe(() => {
const { players, sharedState, playerId, hostId } = client.getSnapshot();
// Re-render your game
});
// Update shared state (host only)
if (client.isHost) {
client.updateSharedState({ score: 100 });
}
// Update your player state
client.updateMyState({ x: player.x, y: player.y });
// Send events
client.sendEvent("shoot", { angle: 45 });
// Read state directly (no subscription needed in game loops)
const { players, sharedState } = client;
// Clean up
client.destroy();
class GameScene extends Phaser.Scene {
private client!: MultiplayerClient;
create() {
this.client = new MultiplayerClient({
host: "https://vibedgames-party.kyh.workers.dev",
party: "vg-server",
room: "phaser-room",
});
}
update() {
// Read other players
for (const [id, player] of Object.entries(this.client.players)) {
if (id === this.client.playerId) continue;
// Render player at player.state.x, player.state.y
}
// Send my position
this.client.updateMyState({ x: this.ship.x, y: this.ship.y });
}
destroy() {
this.client.destroy();
}
}
Host runs simulation, broadcasts via updateSharedState. Others render and send input.
Don't send state every frame. ~20Hz is plenty:
let frame = 0;
function update() {
frame++;
if (frame % 3 === 0) {
client.updateMyState({ x, y });
}
}
npx vibedgames deploy ./dist --slug my-game
Live at https://my-game.vibedgames.com — party server is shared infrastructure.