GameLobby
Attributes
- auto-ready
booleanAutomatically call ready() when a queue match is found- url
.url string | null- reconnect
.reconnect boolean
Methods
- .define(tag, registry)
- .createRoom()
- Create a private room. After a handoff this reconnects first.
- .joinRoom(code)
- Join a room by code. After a handoff this reconnects first.
- .leaveQueue()
- Leave the matchmaking queue.
- .leaveRoom()
- Leave the current private room. The signalling protocol has no leave message, so this drops the WebSocket and reconnects with a new player identity, which the server sees as the player leaving.
- .joinQueue(preferences)
- Enter the matchmaking queue with optional preference strings. The intent survives until a match starts signalling or `leaveQueue()` is called: a dropped socket or a cancelled match re-enters the queue on its own.
- .setPreference(preference)
- Set a preference string (e.g. chosen role, faction).
- .ready()
- Mark self as ready within a room.
- .unready()
- Mark self as not ready within a room.
- .handoff()
- Release the signalling socket once the peer connection is up. The server
drops this player from the room without telling the others, and closes
the room when the last player hands off. Called by
`
`; only valid during signalling. - .reportResult(opponent, outcome)
- Report a match result. Reconnects first if the socket was handed off; the report carries the match token from `start_signalling`.
- .subscribe(context, callback)
Events
- game-lobby-connected
GameLobbyConnectedEvent - Connected and assigned a player ID
- game-lobby-room
GameLobbyRoomEvent - Room created or joined
- game-lobby-match
GameLobbyMatchEvent - Match found in queue
- game-lobby-queue
GameLobbyQueueEvent - Queue position updated
- game-lobby-player
GameLobbyPlayerEvent - Player joined/left/readied in room
- game-lobby-start
GameLobbyStartEvent - Signalling phase beginning
- game-lobby-error
GameLobbyErrorEvent - Server error received
Custom States
| State | Description |
|---|---|
:state(connecting) | Connecting to server |
:state(connected) | WebSocket open, no room or queue |
:state(in-room) | In a room, waiting for ready |
:state(in-queue) | In matchmaking queue |
:state(signalling) | WebRTC signalling in progress |
:state(handed-off) | Peers connected, socket released until a result is reported |
:state(disconnected) | WebSocket closed |
Manages a WebSocket connection to a signalling server. Handles matchmaking
queue, private rooms, player ready states, and relays SDP/ICE messages to
<game-peer-connection> for WebRTC establishment.
Pushes lobby state into shell stats on every transition, making it
available to <game-signal> and when-* conditions throughout the shell
tree.
Attributes
- auto-ready
booleanAutomatically call ready() when a queue match is found- url
.url string | null- reconnect
.reconnect boolean
- url
-
string— WebSocket URL of the signalling server (without the/ws/{game-id}path, which is appended automatically). Defaults tohttps://signalling.htmlgamekit.dev. Override to point at your own server. No connection is made until the parent shell has agame-id. - reconnect
-
boolean— When present, automatically reconnects after 2 seconds if the WebSocket closes unexpectedly. Omit for single-attempt connections. - auto-ready
-
boolean— When present, calls.ready()automatically as soon as a queue match is found, so queued players skip a manual accept step. Has no effect on the private room flow, where readiness is the player's decision.
Instance Methods
- .joinQueue(preferences?)
-
Enter the matchmaking queue. The server pairs players in the same queue
(same shell
game-id). Firesgame-lobby-queuewhen confirmed;game-lobby-startwhen a match is found and signalling begins.Parameters:
preferences—string[]— Optional preference strings (e.g.["role:healer"]) sent to the server for match filtering.
lobby.joinQueue(); lobby.joinQueue(["difficulty:hard"]); - .leaveQueue()
-
Leave the matchmaking queue before a match is found. The element returns to
:state(connected)immediately rather than waiting for the server, so a queue overlay selected withwhen-eq-lobby-state="in-queue"closes at once. - .leaveRoom()
-
Leave the current private room. The signalling protocol has no leave
message, so this closes the WebSocket and reconnects with a fresh player
identity — the server sees the player leave, and the room code and player
count stats are cleared. Called automatically when the shell quits
(
shell.quit()/--quit) while in a room or during signalling, so the other player receivesgame-lobby-playerwith action"left". After a handoff the room no longer exists, so quitting reconnects with the same identity instead. - .handoff()
-
Release the signalling socket once the peer connection is up. Sends
handoffso the server drops this player from the room without telling the others, then closes the WebSocket and moves to:state(handed-off). No reconnect is scheduled: the nextcreateRoom(),joinRoom(),joinQueue(),reportResult()or shell quit reconnects with the same player ID and carries on. Called by<game-peer-connection>once both peers have finished ICE gathering; ignored unless the lobby is insignalling. - .createRoom()
-
Create a private room. Fires
game-lobby-roomwith the room code on success. Share the code (via<game-signal key="room-code">) with another player so they can join. - .joinRoom(code)
-
Join a private room by code. Fires
game-lobby-roomon both the host and the joiner.Parameters:
code—string— Room code (case-insensitive; the server normalises it).
lobby.joinRoom("ABCD1234"); - .ready()
-
Signal that this player is ready to start. The server sends
StartSignallingwhen all players in the room have readied up, which causesgame-lobby-startto fire and `` to begin WebRTC establishment. - .unready()
- Withdraw the ready signal.
- .setPreference(preference)
-
Update this player's preference string. Other players in the room receive
game-lobby-playerwith action"preference".Parameters:
preference—string
- .reportResult(opponent, outcome)
-
Report a match outcome to the server for rating/ranking. Reconnects with
the same player ID first if the socket was handed off, and sends the match
token issued at
start_signalling. Does nothing before a match has started signalling.Parameters:
opponent—string— The opponent's player ID.outcome—"win" | "loss" | "draw"
lobby.reportResult(match.peerId, "win");
Events
- game-lobby-connected
-
Fires when the WebSocket opens and the server assigns a player ID.
Property Type Description playerIdstringAssigned player ID - game-lobby-room
-
Fires when a room is created (
createRoom) or joined (joinRoom).Property Type Description codestringRoom code playersArray<{ id: string, preference: string|null }>Current players - game-lobby-player
-
Fires when a player joins, leaves, readies, unreadies, or changes preference
in the current room.
Property Type Description action"joined" | "left" | "ready" | "unready" | "preference"What happened player{ id: string, preference?: string|null }The player - game-lobby-queue
-
Fires when the server confirms queue entry.
Property Type Description positionnumberPosition in the queue - game-lobby-match
-
Fires when a match is found (queue flow only), before signalling begins.
Property Type Description playersArray<{ id: string, preference: string|null }>Matched players - game-lobby-start
-
Fires when signalling begins (both queue and room flows). `
` starts WebRTC establishment automatically when this fires. Property Type Description playersArray<{ id: string, preference: string|null }>Players in the match codestring|nullRoom code if in a private room - game-lobby-error
-
Fires when the server sends an error.
Property Type Description codestringError code messagestringHuman-readable message
All events bubble and compose, so you can listen at game-shell level.
Commands
Buttons drive the lobby declaratively with the native Invoker Commands API —
commandfor names the lobby element, command names the action:
| Command | Effect |
|---|---|
--create-room |
.createRoom() |
--join-room |
.joinRoom(value) |
--leave-room |
.leaveRoom() |
--join-queue |
.joinQueue(value.split(/\s+/)) |
--leave-queue |
.leaveQueue() |
--lobby-ready |
.ready() |
--lobby-unready |
.unready() |
--set-preference |
.setPreference(value) |
--join-room, --join-queue and --set-preference read the button's
value attribute; the others ignore it.
<button commandfor="lobby" command="--create-room">Create private room</button>
<button commandfor="lobby" command="--join-queue" value="ranked">
Find a ranked game
</button>
CSS States
| State | When active |
|---|---|
:state(connecting) |
WebSocket connect in progress |
:state(connected) |
WebSocket open, not in room or queue |
:state(in-room) |
In a private room |
:state(in-queue) |
In the matchmaking queue |
:state(signalling) |
WebRTC signalling in progress |
:state(disconnected) |
WebSocket closed |
game-lobby:state(connecting)::after {
content: "Connecting...";
}
game-lobby:state(connected)::after {
content: "Connected";
}
Shell Stats
The lobby writes to shell stats on every state change, making them
available to <game-signal> and when-* conditions without any custom code:
| Stat | Type | Description |
|---|---|---|
lobby-state |
string |
Current state name |
player-count |
number |
Players in the current room |
room-code |
string |
Room code when in a room |
queue-position |
number |
Queue position when waiting |
player-id |
string |
This player's server-assigned ID |
<p>Room: <game-signal key="room-code"></game-signal></p>
<div when-eq-lobby-state="in-queue" data-overlay>Finding a game...</div>
game-shell.start() clears stats, so the lobby republishes all of the above
on the "setup" lifecycle event. Lobby state therefore stays readable during
playing without any bookkeeping in your game.
Signal Access
| Signal | Usage |
|---|---|
.playerId |
Signal.State<string|null> — current player ID, consumed by <game-peer-connection> |
.startSignalling |
Signal.State<{ players, code }|null> — consumed by <game-peer-connection> |
These are public Signal.State instances on the element instance. They are
the primary interface between <game-lobby> and <game-peer-connection> — you do
not need to access them directly.
Usage
<game-shell id="game" game-id="my-game" rounds="1">
<game-lobby id="lobby" reconnect auto-ready></game-lobby>
<game-peer-connection auto-ready></game-peer-connection>
<div
when-some-scene="intro"
when-no-lobby-state="in-room in-queue signalling"
data-overlay
>
<button commandfor="lobby" command="--join-queue">Find a game</button>
<button commandfor="lobby" command="--create-room">
Create private room
</button>
</div>
<div when-some-scene="intro" when-eq-lobby-state="in-room" data-overlay>
<p>Room: <game-signal key="room-code"></game-signal></p>
<button commandfor="lobby" command="--lobby-ready">Ready</button>
<button commandfor="lobby" command="--leave-room">Cancel</button>
</div>
</game-shell>
With auto-ready on <game-peer-connection> the shell starts by itself once
both peers are connected, so this whole flow needs no JavaScript.
See the Multiplayer concept page and the Noughts & Crosses tutorial for complete integration examples.