Funday play screen — visual cheat sheet
Plain-language map of what you see on /play/[gameId] and how the pieces talk to each other.
One screen, four layers (bottom → top)
🧱 Game (iframe or native Svelte game)
→ lives in the big rectangle; this is the actual title you launched.
📊 HUD (optional small overlay)
→ tiny status chips when the game reports something (mode, latency, etc.).
🗂️ Right drawer / Bottom sheet (desktop right panel, mobile bottom overlay)
→ multiplayer lobby list, match card, start/leave, and chat.
→ Nakama socket + match logic mostly live behind this panel.
🚢 Bottom dock (full-width bar)
→ Lobby button (opens drawer), game-specific actions (restart, howto, …), leaderboard peek, feedback, fullscreen, exit.
🧭 Top navbar (site chrome)
→ not part of the game; play area starts below it (--nav-h).
Visual layout (desktop, drawer open)
--- config: theme: forest look: handDrawn --- flowchart TB %% 🎨 Severity hints on concepts classDef regular fill:#bbf7d0,stroke:#166534,color:#14532d classDef watch fill:#fde68a,stroke:#b45309,color:#78350f classDef hot fill:#fecaca,stroke:#b91c1c,color:#7f1d1d NAV["🧭 Navbar<br/>site chrome"]:::regular subgraph MAIN["🖼️ Play column (insets when drawer open)"] VP["🧱 GameViewport<br/>iframe / native"]:::regular HUD["📊 GameHUD<br/>optional chips"]:::regular end DR["🗂️ GameDrawer<br/>lobby · match · chat"]:::watch DK["🚢 GameDock<br/>icons + platform"]:::regular NAV --> MAIN MAIN --> DK DR -. overlaps right .- MAIN TOK["📐 Tokens<br/>--nav-h --dock-h --drawer-w"]:::hot TOK -.-> MAIN TOK -.-> DK TOK -.-> DR
On small phones the drawer converts into a bottom sheet overlay; it does not squeeze the main column.
CSS variables (geometry only)
These are ruler marks the layout uses; you do not need to memorize them.
📏 --nav-h — space reserved under the navbar (default 64px).
📏 --dock-h — height of the bottom dock; GameDock measures itself and writes this so the game is not hidden behind the bar.
📏 --drawer-w — width of the right drawer (default 24rem). When the drawer is open on tablet/desktop, the main column and the dock shrink from the right by this amount so nothing sits under the drawer.
Mental model: two “remote controls”
🎮 Game → Host (SvelteKit)
postMessage bridge: the iframe sends things like funday:dock:set to add buttons to the dock.
🏠 Host → Game
the shell can send session, match state, fullscreen hints, etc.
So: dock buttons are often “declared” by the game, then rendered by GameDock.svelte.
--- config: theme: forest look: handDrawn --- sequenceDiagram autonumber participant G as 🎮 Game embed participant B as 🔌 Bridge participant C as 📦 gameContext participant D as 🚢 GameDock participant W as 🗂️ GameDrawer participant N as 🌐 Nakama G->>B: funday:dock:set {actions} B->>C: merge actions C->>D: render icon buttons + title tooltip D->>W: openDrawer('lobby') on Lobby click W->>N: list / join / match data N-->>W: presences, chat W->>G: funday:match-state (via postToGame)
Lobby vs match vs chat (inside the drawer)
🟦 Lobby phase — you see open matches / create / join (or dedicated-server flows).
Chat channel name is scoped to the game lobby.
🟩 In a match, not playing yet — match card, host can start when rules allow.
Chat can stay “lobby” until someone toggles or the game goes playing.
🟥 Match playing — chat can auto-switch to “match room” so talk is per-session; header toggles let you force lobby vs match context.
--- config: theme: forest look: handDrawn --- stateDiagram-v2 [*] --> Closed: load /play Closed --> OpenLobby: 🖱️ tap Lobby on dock<br/>or auto-open MP OpenLobby --> Closed: ✖ close drawer<br/>match stays connected OpenLobby --> OpenLobby: drawer shows<br/>lobby list + chat note right of OpenLobby Lobby BUTTON stays on bottom dock (left area). CONTENT of lobby lives in the right drawer. Visual link: active / pressed state on Lobby while open (recommended polish). end note
Z-order (who paints on top)
Rough stacking: game at the back → HUD → dock (mid) → drawer higher → modals/toasts highest.
If something looks “under” something else, it is almost always a z-index or pointer-events issue, not game logic.
Single-player vs multiplayer
🧍 max players ≤ 1 → Lobby action is removed from the dock (no matchmaking UI needed).
👥 max players > 1 → Lobby is the primary dock button; drawer may auto-open on first load (some games are excluded, e.g. scribblaz, splix).
Quick debugging checklist
🔌 Socket not ready → drawer actions that need Nakama may stall; check auth / socketStore.ready.
📬 ?join= / ?autostart= on URL → play page opens drawer and consumes params (invite / solo / practice flows).
📐 Game canvas clipped at bottom → --dock-h should be non-zero after dock mounts; if zero, the dock may not be measuring.
↔️ Drawer open but dock looks wrong on desktop → dock and play-root should both inset with --drawer-w (aligned layout).
Where to read code (when you are ready)
frontend/src/routes/play/[id]/+page.svelte — stacks viewport, HUD, dock, drawer.
frontend/src/lib/components/games/GameDock.svelte — bottom bar + --dock-h.
frontend/src/lib/components/games/GameDrawer.svelte — desktop right panel / mobile bottom sheet + match + chat.
frontend/src/lib/components/games/GameViewport.svelte — iframe/native + bridge.
frontend/src/lib/stores/gameDrawer.svelte.ts — open/close/mode/pending join.
frontend/src/lib/stores/gameContext + lobbyState.svelte.ts — shared shell state.
One-sentence summary
The play route is a picture frame: game in the middle, shell chrome on bottom and right (desktop), with measured spacers (--dock-h, --drawer-w) so the game always stays visible and aligned.
Severity color code (issues & ideas)
🟢 Regular — polish, consistency, docs; safe to schedule anytime.
🟡 Pressing — layout confusion, discoverability, two sources of truth; fix next sprint.
🔴 Hot — breaks trust or gameplay (chat wrong channel, dock under drawer, clipped canvas); fix before shipping features on top.
--- config: theme: forest look: handDrawn --- pie showData title 🌡️ Where UX pain clusters (conceptual) "🟢 Clear icon + tooltip pattern" : 35 "🟡 Drawer / dock / fullscreen edge cases" : 40 "🔴 Chat SSOT across game + shell" : 25
Shared dock UI (many games expect the same icons)
🌍 Icons are international; always pair icon with title / tooltip (aria-label + visible data-tip on hover).
🏠 Lobby — opens the drawer (list + chat). The button stays on the bottom dock (left cluster); the panel slides in. Recommended polish: Lobby looks “pressed” or primary while the drawer is open.
🔁 Restart / Reset — round or full reset; games declare via funday:dock:set.
ℹ️ Howto / Help — rules overlay.
🔊 Volume — mute / unmute.
⚙️ Settings — in-game options.
⛶ Fullscreen — browser fullscreen + optional game:fullscreen to the embed.
✖ Exit — leave play route.
--- config: theme: forest look: handDrawn --- mindmap root((🚢 Universal dock)) 🏠 Lobby opens right drawer icon Clipboard 🔁 Restart / Reset confirm destructive ℹ️ Howto / Help modal or overlay 🌐 Online / Reconnect network sanity 🔊 Volume mute toggle ⚙️ Settings per-game prefs 🏆 Leaderboard platform mini LB 💬 Feedback bugs / praise ⛶ Fullscreen immersive shell ✖ Exit history.back
“Lobby location” — what actually moves
The lobby UI lives in the drawer, which is anchored to the right edge on desktop and bottom edge on mobile. The Lobby control on the dock does not need to physically move to follow it: users learn “tap bottom-left-of-bar → panel appears.” Optional upgrade (🟡): add a slim vertical “Lobby” tab on the right edge (desktop) when the drawer is closed, aligned with where the drawer opens (stronger spatial affordance).
Player journey (find multiplayer)
--- config: theme: forest look: handDrawn --- journey title 🎯 Get into a match section Land Open game from catalog: 5: Player section Dock See 🏠 Lobby icon + tooltip: 4: Player Tap Lobby: 5: Player section Drawer New / Join / Refresh: 4: Player Read lobby chat: 3: Player section Match Host starts: 5: Player Match chat auto: 4: Player
Immersive / Scribblaz-style fullscreen (vision)
🎯 Goal: maximum canvas; chrome appears only on intent.
↖️ Top-left: menu icon restores top navbar.
↘️ Bottom-right: avatar strip or compact chat handle; tap expands transient bubbles.
📌 Pinned drawer: optional mode — drawer stays open as a semi-transparent rail; unpinned = overhang overlay.
💭 Chat bubbles that fade: good for guess-heavy games; still keep one Nakama SSOT (match vs lobby channel) in the shell — the game only renders decorative bubbles, not a second chat backend.
--- config: theme: forest look: handDrawn --- flowchart LR classDef regular fill:#bbf7d0,stroke:#166534 classDef watch fill:#fde68a,stroke:#b45309 classDef hot fill:#fecaca,stroke:#b91c1c FS["⛶ Fullscreen / minimal chrome"]:::watch FS --> TL["↖️ Top-left<br/>☰ reopen top bar<br/>icon only"]:::regular FS --> BR["↘️ Bottom-right<br/>👤 / 💬 strip<br/>peek chat"]:::regular FS --> PIN["📌 Pin drawer<br/>opaque vs glass"]:::hot FS --> CHAT["💭 Bubbles fade<br/>Scribblaz guesses"]:::hot CHAT -. SSOT .- N["🌐 Nakama channels<br/>lobby vs match"]:::regular