Minimal WebSocket relay server for party games. Clients share rooms and exchange messages — the server just forwards them.
- A client creates a room (server assigns a 4-char code) with a max client limit
- Other clients join by room code
- Clients provide their own UUID — reconnecting with the same UUID replaces the old connection
- Messages can be broadcast to all peers or sent to a specific client
peer_leftis broadcast immediately on disconnect- Rooms are cleaned up when empty
bun run server.ts
# or
PORT=8080 bun run server.tsdocker build -t party-sockets .
docker run -p 3000:3000 party-socketsconst ws = new WebSocket("wss://party-sockets.duckdns.org");
const clientId = crypto.randomUUID(); // any unique string worksws.send(JSON.stringify({ type: "create", clientId, maxClients: 4 }));ws.send(JSON.stringify({ type: "join", clientId, room: "A3KX" }));// Broadcast to all peers
ws.send(JSON.stringify({ type: "send", data: { move: "left" } }));
// Send to a specific client
ws.send(JSON.stringify({ type: "send", to: "uuid-of-target", data: "hello" }));ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case "created": // room created, msg.room is the 4-char code
case "joined": // joined room, msg.clients is the list of client IDs
case "peer_joined": // new peer, msg.clientId
case "peer_left": // peer disconnected, msg.clientId
case "message": // relayed message, msg.from + msg.data
case "error": // error, msg.message
}
};Joining with the same clientId replaces the old connection — no special reconnect message needed.
sequenceDiagram
participant A as Client A
participant S as Server
participant B as Client B
Note over A,B: Create a room
A->>S: { type: "create", clientId: "aaa", maxClients: 4 }
S->>A: { type: "created", room: "A3KX" }
Note over A,B: Join a room
B->>S: { type: "join", clientId: "bbb", room: "A3KX" }
S->>B: { type: "joined", room: "A3KX", clients: ["aaa", "bbb"] }
S->>A: { type: "peer_joined", clientId: "bbb" }
Note over A,B: Broadcast message
A->>S: { type: "send", data: { move: "left" } }
S->>B: { type: "message", from: "aaa", data: { move: "left" } }
Note over A,B: Targeted message
B->>S: { type: "send", to: "aaa", data: "hello" }
S->>A: { type: "message", from: "bbb", data: "hello" }
Note over A,B: Disconnect
B--xS: connection closed
S->>A: { type: "peer_left", clientId: "bbb" }
All messages are JSON over WebSocket.
| type | fields | description |
|---|---|---|
create |
clientId, maxClients |
Create a new room |
join |
clientId, room |
Join an existing room |
send |
data, to? |
Send to all peers or a specific client |
| type | fields | description |
|---|---|---|
created |
room |
Room created successfully |
joined |
room, clients[] |
Joined room, list of current client IDs |
peer_joined |
clientId |
A new peer joined the room |
peer_left |
clientId |
A peer disconnected |
message |
from, data |
Relayed message from a peer |
error |
message |
Error description |
bun test