Files
fullhouse/PROPOSAL.md
Fredrik Johansson d99c44cb9d
All checks were successful
Docker / build-and-push (push) Successful in 1m18s
Initial build: fullhouse, live music bingo from a real Spotify playlist
Based on bingo-bango by f8al (MIT) — same core card-generation idea
("both facets": a square is either an exact song title or an artist,
and an artist square lights up on any of their tracks), reworked with
a real host/guest split, live multiplayer sessions, and cover-art
squares. f8al credited in the footer, README, and PROPOSAL.md.

Three ways to play: host a live session (server-side Spotify
connection, join code + QR, caller screen, auto-call from real
playback), join someone else's session (no login needed), or solo
(guest PKCE login or a pasted public playlist URL, one-off card).

Genuinely verified against a real Spotify account throughout, not
just built and assumed working — including two real bugs found and
fixed along the way (Spotify's playlist-tracks endpoint quietly
renamed to /items with a reshaped response; reading playlist tracks
needs real user auth even for public playlists, so the "no guest
login" public-playlist path routes through the host's connection
instead of a dead-end app-only token). Both Docker images built and
run together on a real network with the nginx proxy verified working
end-to-end, and auto-call confirmed detecting an actual track playing
live through Spotify Connect within one poll tick.
2026-07-18 22:05:37 +02:00

6.2 KiB

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 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.