alepha@docs:~/docs/reference/primitives$
cat $room.md | pretty1 min read
#$room
#Import
typescript
1import { $room } from "alepha/websocket";
#Overview
Defines a stateful WebSocket room — the home for an authoritative, optionally tick-driven simulation.
Where {@link $websocket} is a stateless, per-message pub/sub handler, $room
holds in-memory state across messages and drives a server-side tick loop. It
is the primitive a real-time game server is built on:
tickHz > 0→ an authoritative simulation room. The loop runs only while a socket is connected (an empty room costs nothing), callingonTick(room, dt)every1000/tickHzms. Inside a tick you readroom.state, iterateroom.connections, and push per-recipient frames withroom.send(id, msg)orroom.broadcast(msg).tickHzomitted → a headless coordinator addressed by id and reached through server-sidemethods. This is how you model cross-room party state or a single-owner presence lease.
Same code runs on Node (one process, real timers) and on Cloudflare (one
Durable Object per channelPath:roomId, the loop alive while the socket is).
#Examples
An authoritative 20Hz world
typescript
1class GameServer { 2 world = $channel({ path: "/ws/world", schema: { in: serverMsg, out: clientIntent } }); 3 4 room = $room({ 5 channel: this.world, 6 tickHz: 20, 7 state: () => new World(), 8 onJoin: (room, conn) => room.state.addPlayer(conn.id), 9 onMessage: (room, conn, intent) => room.state.enqueue(conn.id, intent),10 onTick: (room, dt) => {11 room.state.step(dt);12 for (const conn of room.connections) room.send(conn.id, room.state.viewFor(conn.id));13 },14 onLeave: (room, conn) => room.state.removePlayer(conn.id),15 onEmpty: (room) => room.state.persist(),16 });17}
A headless coordinator (party-wide state)
typescript
1class Coordinator { 2 party = $channel({ path: "/ws/party", schema: { in: any, out: any } }); 3 4 session = $room({ 5 channel: this.party, 6 state: () => ({ switches: {} as Record<string, boolean> }), 7 methods: { 8 setSwitch: (room, id: string, value: boolean) => { room.state.switches[id] = value; }, 9 snapshot: (room) => room.state.switches,10 },11 });12 13 async flip(partyId: string, id: string) {14 await this.session.call(partyId, "setSwitch", id, true);15 }16}