# Proposal: `fullhouse` — music bingo, live-session and self-service **Status:** proposed, build in progress. Self-contained — written so a fresh agent with no prior conversation context can pick this up and implement it without anything explained first. ## Origin & attribution Inspired by [bingo-bango](https://github.com/f8al/bingo-bango) by **f8al** — a fully client-side (Spotify PKCE, no backend) music bingo card generator. `fullhouse` keeps the core idea (Spotify playlist → bingo cards, "both facets" title+artist marking) and the card-generation algorithm's shape, but changes the architecture significantly enough that it's a fresh build, not a fork: a real server-side host mode, live multiplayer sessions, cover-art squares, and heavier use of the Spotify API. MIT license from the original requires the copyright notice + license text be retained wherever the original code/algorithm is directly reused — credit f8al clearly in the README and footer regardless of how much code ends up shared verbatim. ## Motivation The original requires *every player* to individually log into Spotify to pick a playlist — real friction at an actual game night where guests don't want to OAuth their own account just to get a bingo card. The fix: a proper host/guest split. ## Modes Both exist simultaneously, chosen per session, not one replacing the other: 1. **Host mode** — the host (the site owner) has a real server-side Spotify connection (same shape as `goonk`'s existing `spotify.mjs`: Authorization Code flow, refresh token persisted server-side, no per-visitor login). Host picks from *their own* playlists (`playlist-read-private` + `playlist-read-collaborative` scopes) as the song pool for a session. 2. **Guest self-service mode** — the original flow, kept: a visitor with no relationship to the host logs in via their own Spotify PKCE flow and generates a card from their own playlist, standalone, no session/host involved. Also supports pasting a public playlist URL with **no login at all**, via an app-level Client Credentials token (public playlists don't need user auth to read) — simpler than PKCE for the common "just grab a public playlist" case, and something the original doesn't offer. ## The four differentiators (all in scope for this build) 1. **Host-curated mode** — described above. Solves the real friction. 2. **Live session mode** — host starts a session, gets a join code + QR. Guests join via the code (no login required in a host-curated session — the host already authorized the song pool), each gets an independently seeded card. A "caller" screen shows the current track large, for the host to read aloud or leave visible on a shared screen. State sync is **polling**, not WebSockets — deliberately, matching this whole family's precedent (`keep watch`, `wisp`'s sweep): a session isn't latency- sensitive enough to justify the operational complexity of a socket server. Poll every 3-5s for "what's been called." 3. **Live now-playing auto-call** — if the host is actually playing the session's pool through Spotify Connect, poll `/me/player/currently- playing` (same pattern as `goonk`'s `now-playing` route) and auto-mark a track "called" the moment it starts, matched against the pool by track ID. Falls back to a manual "call next" button (classic random-draw-without-replacement bingo caller) for hosts not using live Spotify Connect playback during the game — auto-call is a bonus layer over manual, not a replacement for it. 4. **Cover-art squares** — squares can show album art (from the same playlist-track API response, `track.album.images`, already fetched for everything else) instead of, or alongside, text. A visual variant beyond what the original offers. ## Architecture **Server** (small Express app, same shape as `goonk`'s `api/`): - Host OAuth: `/auth/login`, `/auth/callback`, refresh-token persistence — ported near-verbatim in pattern (not code) from `goonk/api/routes/ spotify.mjs`, since that flow is already proven correct in this family. - `GET /api/host/playlists` — the host's own playlists, cached. - `GET /api/host/playlist/:id/tracks` — full track+artist+album-art data for a chosen playlist, cached. - `GET /api/public-playlist?url=` — Client Credentials app token, fetch a *public* playlist's tracks with no user login. Client Credentials tokens are cached in memory and refreshed on expiry, same shape as the existing user-token cache pattern. - Session state: `POST /api/session` (host creates one from a chosen playlist + mode), `GET /api/session/:code` (guest join — hands back pool metadata, NOT the host's Spotify tokens), `POST /api/session/:code/call` (host marks a track called, or the auto-call poller does), `GET /api/ session/:code/state` (guests poll this for the called-track list). In-memory session store is fine — sessions are ephemeral (a single game night), no need for persistence across restarts. - `GET /api/host/now-playing` — thin wrapper the auto-call poller uses, same call shape as `goonk`'s existing now-playing route. **Client**: Vite + TypeScript, no framework — matches the `typo`/`latent` family's "plain, no heavy build" ethos more than the original's React choice. The card-generation engine is a pure, dependency-free TS module (same shape as the original's `src/cards/`), extended with a `coverArt` render mode alongside the text mode. **Guest PKCE mode** stays fully client-side (no server involvement at all) for the self-service path — same as the original, since that's the whole point of PKCE (no secret needed, tokens never touch a server the host controls). ## Theming Dark/mono/green house style shared with `typo`/`flit`/`wisp` (see any of those `style.css` files for the exact CSS custom properties), not the original's own visual design. Reads as part of this family, not a standalone fork. ## Non-goals - No accounts beyond the host's own Spotify connection — guests never create anything, a join code is the only credential a session needs. - No persistent session history — a session lives in memory for its game night and is gone after. - Not trying to be a general Spotify app — scoped tightly to bingo card generation + live session calling.