Skip to content

Instantly share code, notes, and snippets.

@JKHeadley
Created April 3, 2026 01:46
Show Gist options
  • Select an option

  • Save JKHeadley/c23fc90107a85b715f6095c8be9fb7de to your computer and use it in GitHub Desktop.

Select an option

Save JKHeadley/c23fc90107a85b715f6095c8be9fb7de to your computer and use it in GitHub Desktop.
B2Lead Film Score Quiz - Name That Tune
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Name That Tune! - B2Lead Film Score Day</title>
<link href="https://fonts.googleapis.com/css2?family=Bangers&family=Inter:wght@400;600;700;900&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Inter', sans-serif; background: linear-gradient(135deg, #0f0c29, #1a1a4e, #24243e); color: #fff; min-height: 100vh; overflow-x: hidden; }
.screen { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
.team-a-bg { background: linear-gradient(135deg, #F59E0B, #D97706) !important; color: #1a1a2e !important; }
.team-b-bg { background: linear-gradient(135deg, #06B6D4, #0891B2) !important; color: #1a1a2e !important; }
.team-a-accent { border-left: 4px solid #F59E0B; }
.team-b-accent { border-left: 4px solid #06B6D4; }
.team-a-glow { color: #F59E0B; text-shadow: 0 0 30px rgba(245,158,11,0.5); }
.team-b-glow { color: #06B6D4; text-shadow: 0 0 30px rgba(6,182,212,0.5); }
.join-container { text-align: center; max-width: 480px; width: 100%; }
.music-icon { font-size: 64px; display: block; margin-bottom: 10px; animation: float 3s ease-in-out infinite; }
@keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
.game-title { font-family: 'Bangers', cursive; font-size: 56px; letter-spacing: 3px; background: linear-gradient(90deg, #F59E0B, #EF4444, #06B6D4); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin-bottom: 5px; }
.game-subtitle { font-size: 18px; color: rgba(255,255,255,0.6); margin-bottom: 40px; }
.name-input { width: 100%; padding: 16px 20px; font-size: 20px; border: 2px solid rgba(255,255,255,0.2); border-radius: 12px; background: rgba(255,255,255,0.1); color: #fff; text-align: center; outline: none; transition: border-color 0.3s; margin-bottom: 20px; }
.name-input:focus { border-color: #06B6D4; }
.name-input::placeholder { color: rgba(255,255,255,0.4); }
.team-label { font-size: 16px; color: rgba(255,255,255,0.7); margin-bottom: 15px; }
.team-buttons { display: flex; gap: 15px; justify-content: center; }
.team-btn { flex: 1; padding: 20px 15px; border: none; border-radius: 16px; cursor: pointer; font-size: 18px; font-weight: 700; transition: transform 0.2s, box-shadow 0.2s; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.team-btn:hover { transform: translateY(-3px); box-shadow: 0 8px 25px rgba(0,0,0,0.3); }
.team-btn:active { transform: translateY(0); }
.team-a-btn { background: linear-gradient(135deg, #F59E0B, #D97706); color: #1a1a2e; }
.team-b-btn { background: linear-gradient(135deg, #06B6D4, #0891B2); color: #1a1a2e; }
.team-emoji { font-size: 28px; }
.team-name { font-size: 16px; }
.error-text { color: #EF4444; margin-top: 15px; min-height: 20px; }
.player-container { text-align: center; max-width: 480px; width: 100%; display: flex; flex-direction: column; align-items: center; gap: 15px; }
.player-header { display: flex; align-items: center; gap: 12px; justify-content: center; flex-wrap: wrap; }
.round-badge { background: rgba(255,255,255,0.15); padding: 8px 18px; border-radius: 20px; font-weight: 700; font-size: 16px; }
.round-badge.large { font-size: 22px; padding: 10px 24px; }
.team-badge { padding: 6px 14px; border-radius: 20px; font-weight: 700; font-size: 14px; }
.hint-text { font-style: italic; color: rgba(255,255,255,0.7); font-size: 18px; min-height: 24px; }
.status-text { font-size: 20px; font-weight: 700; min-height: 30px; transition: color 0.3s; }
.status-text.waiting { color: rgba(255,255,255,0.5); }
.status-text.active { color: #10B981; animation: pulse-text 1s infinite; }
.status-text.buzzed { color: #F59E0B; }
@keyframes pulse-text { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
.buzz-btn { width: 220px; height: 220px; border-radius: 50%; border: none; background: linear-gradient(145deg, #666, #444); color: rgba(255,255,255,0.4); font-family: 'Bangers', cursive; font-size: 48px; cursor: not-allowed; transition: all 0.3s; position: relative; margin: 10px 0; -webkit-tap-highlight-color: transparent; }
.buzz-btn.active { background: linear-gradient(145deg, #EF4444, #DC2626); color: #fff; cursor: pointer; box-shadow: 0 0 40px rgba(239,68,68,0.4), 0 0 80px rgba(239,68,68,0.2); animation: buzz-pulse 1.5s ease-in-out infinite; }
@keyframes buzz-pulse { 0%, 100% { box-shadow: 0 0 40px rgba(239,68,68,0.4), 0 0 80px rgba(239,68,68,0.2); transform: scale(1); } 50% { box-shadow: 0 0 60px rgba(239,68,68,0.6), 0 0 120px rgba(239,68,68,0.3); transform: scale(1.05); } }
.buzz-btn.active:active { transform: scale(0.95); box-shadow: 0 0 20px rgba(239,68,68,0.3); }
.buzz-text { text-shadow: 2px 2px 4px rgba(0,0,0,0.3); letter-spacing: 4px; }
.buzz-result { flex-direction: column; align-items: center; gap: 5px; }
.position-number { font-family: 'Bangers', cursive; font-size: 72px; color: #10B981; text-shadow: 0 0 20px rgba(16,185,129,0.5); }
.position-text { font-size: 16px; color: rgba(255,255,255,0.7); }
.timer-display { font-family: 'Bangers', cursive; font-size: 36px; padding: 5px 20px; border-radius: 10px; background: rgba(255,255,255,0.1); }
.timer-green { color: #10B981; }
.timer-yellow { color: #F59E0B; }
.timer-red { color: #EF4444; animation: timer-blink 0.5s infinite; }
@keyframes timer-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
.scoreboard-mini { display: flex; gap: 12px; width: 100%; max-width: 340px; margin-top: 10px; }
.score-mini { flex: 1; padding: 12px; border-radius: 12px; text-align: center; display: flex; flex-direction: column; gap: 4px; }
.score-team-name { font-size: 12px; font-weight: 700; }
.score-value { font-family: 'Bangers', cursive; font-size: 32px; }
.host-container { width: 100%; max-width: 1100px; padding: 15px; }
.host-header { text-align: center; margin-bottom: 20px; }
.host-title { font-family: 'Bangers', cursive; font-size: 36px; letter-spacing: 2px; }
.host-badge { background: #EF4444; padding: 4px 14px; border-radius: 8px; font-size: 16px; font-family: 'Inter', sans-serif; vertical-align: middle; margin-left: 10px; }
.host-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.host-left, .host-right { display: flex; flex-direction: column; gap: 15px; }
.round-card, .timer-card, .buzz-list-card, .players-card, .scoreboard-card { background: rgba(255,255,255,0.08); border-radius: 16px; padding: 20px; }
.card-title { font-size: 16px; color: rgba(255,255,255,0.6); margin-bottom: 12px; text-transform: uppercase; letter-spacing: 1px; }
.question-card { margin-top: 15px; }
.question-answer { font-family: 'Bangers', cursive; font-size: 32px; color: #F59E0B; margin-bottom: 5px; }
.question-hint { font-style: italic; color: rgba(255,255,255,0.5); font-size: 16px; margin-bottom: 10px; }
.youtube-link { display: inline-block; background: #EF4444; color: #fff; padding: 10px 20px; border-radius: 8px; text-decoration: none; font-weight: 700; font-size: 16px; transition: background 0.2s; }
.youtube-link:hover { background: #DC2626; }
.timer-card { text-align: center; }
.timer-big { font-family: 'Bangers', cursive; font-size: 64px; line-height: 1; }
.timer-label { font-size: 14px; color: rgba(255,255,255,0.5); text-transform: uppercase; }
.host-controls { display: flex; gap: 10px; flex-wrap: wrap; }
.ctrl-btn { padding: 12px 20px; border: none; border-radius: 10px; font-size: 16px; font-weight: 700; cursor: pointer; transition: transform 0.2s, opacity 0.2s; flex: 1; min-width: 100px; }
.ctrl-btn:hover { transform: translateY(-2px); }
.ctrl-btn:active { transform: translateY(0); }
.start-btn { background: #10B981; color: #fff; }
.stop-btn { background: #EF4444; color: #fff; }
.next-btn { background: #6366F1; color: #fff; }
.reset-btn { background: rgba(255,255,255,0.15); color: #fff; }
.danger-btn { background: rgba(239,68,68,0.2); color: #EF4444; border: 1px solid rgba(239,68,68,0.3); }
.award-section { text-align: center; }
.award-label { font-size: 14px; color: rgba(255,255,255,0.6); margin-bottom: 10px; text-transform: uppercase; }
.award-buttons { display: flex; gap: 10px; }
.award-btn { flex: 1; padding: 14px; border: none; border-radius: 10px; font-size: 16px; font-weight: 700; cursor: pointer; transition: transform 0.2s; }
.award-btn:hover { transform: translateY(-2px); }
.buzz-list { display: flex; flex-direction: column; gap: 8px; max-height: 250px; overflow-y: auto; }
.buzz-entry { display: flex; align-items: center; gap: 12px; padding: 10px 14px; background: rgba(255,255,255,0.05); border-radius: 8px; }
.buzz-position { font-family: 'Bangers', cursive; font-size: 24px; color: #10B981; min-width: 40px; }
.buzz-player { flex: 1; font-weight: 600; font-size: 16px; }
.buzz-team-tag { font-size: 12px; padding: 3px 8px; border-radius: 6px; background: rgba(255,255,255,0.1); font-weight: 600; }
.player-list { display: flex; flex-wrap: wrap; gap: 6px; }
.player-tag { padding: 5px 12px; border-radius: 6px; font-size: 13px; font-weight: 600; background: rgba(255,255,255,0.05); }
.empty-text { color: rgba(255,255,255,0.3); font-style: italic; }
.scoreboard-big { display: flex; align-items: center; gap: 15px; }
.score-big { flex: 1; padding: 15px; border-radius: 12px; text-align: center; }
.score-big-team { display: block; font-size: 13px; font-weight: 700; }
.score-big-value { font-family: 'Bangers', cursive; font-size: 48px; display: block; }
.score-big-vs { font-family: 'Bangers', cursive; font-size: 24px; color: rgba(255,255,255,0.3); }
.host-footer { text-align: center; margin-top: 20px; }
.gameover-container { text-align: center; position: relative; }
.gameover-title { font-family: 'Bangers', cursive; font-size: 72px; letter-spacing: 4px; margin-bottom: 20px; background: linear-gradient(90deg, #F59E0B, #EF4444, #06B6D4); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.winner-text { font-family: 'Bangers', cursive; font-size: 48px; margin-bottom: 30px; }
.scoreboard-final { display: flex; align-items: center; gap: 20px; justify-content: center; }
.score-final { padding: 25px 40px; border-radius: 16px; text-align: center; }
.score-final-team { display: block; font-size: 16px; font-weight: 700; }
.score-final-value { font-family: 'Bangers', cursive; font-size: 72px; display: block; }
.score-final-vs { font-family: 'Bangers', cursive; font-size: 32px; color: rgba(255,255,255,0.3); }
.confetti-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; overflow: hidden; z-index: 999; }
.confetti-piece { position: absolute; top: -10px; width: 10px; height: 20px; border-radius: 2px; animation: confetti-fall linear forwards; }
@keyframes confetti-fall { 0% { transform: translateY(0) rotate(0deg); opacity: 1; } 100% { transform: translateY(100vh) rotate(720deg); opacity: 0; } }
@media (max-width: 768px) { .host-grid { grid-template-columns: 1fr; } .game-title { font-size: 42px; } .buzz-btn { width: 180px; height: 180px; font-size: 40px; } .gameover-title { font-size: 48px; } .winner-text { font-size: 32px; } .score-final { padding: 15px 25px; } .score-final-value { font-size: 48px; } }
@media (max-width: 400px) { .game-title { font-size: 36px; } .buzz-btn { width: 160px; height: 160px; font-size: 36px; } .team-buttons { flex-direction: column; } }
</style>
</head>
<body>
<div id="joinScreen" class="screen">
<div class="join-container">
<div class="logo-area">
<span class="music-icon">🎬</span>
<h1 class="game-title">Name That Tune!</h1>
<p class="game-subtitle">B2Lead Film Score Day</p>
</div>
<div class="join-form">
<input type="text" id="playerName" placeholder="Enter your name" class="name-input" maxlength="30" autocomplete="off">
<p class="team-label">Pick your team:</p>
<div class="team-buttons">
<button class="team-btn team-a-btn" onclick="joinGame('Team A')"><span class="team-emoji">🎯</span><span class="team-name">Team A</span></button>
<button class="team-btn team-b-btn" onclick="joinGame('Team B')"><span class="team-emoji">🌟</span><span class="team-name">Team B</span></button>
</div>
<p id="joinError" class="error-text"></p>
<p style="margin-top:30px"><a href="#" onclick="enterHostMode(); return false;" style="color:rgba(255,255,255,0.15); font-size:12px; text-decoration:none;">Host Mode</a></p>
</div>
</div>
</div>
<div id="playerScreen" class="screen" style="display:none">
<div class="player-container">
<div class="player-header">
<div class="round-badge">Round <span id="pRoundNum">1</span> of <span id="pTotalRounds">10</span></div>
<div id="pTeamBadge" class="team-badge"></div>
</div>
<div id="pHint" class="hint-text" style="display:none"></div>
<div id="pStatus" class="status-text waiting">Waiting for host to start the round...</div>
<div id="pTimerDisplay" class="timer-display" style="display:none"><span id="pTimer">30</span>s</div>
<button id="buzzButton" class="buzz-btn" onclick="buzz()" disabled><span class="buzz-text">BUZZ!</span></button>
<div id="buzzResult" class="buzz-result" style="display:none">
<span id="buzzPosition" class="position-number"></span>
<span id="buzzMessage" class="position-text"></span>
</div>
<div class="scoreboard-mini">
<div class="score-mini team-a-bg"><span class="score-team-name">Team A</span><span id="pScoreA" class="score-value">0</span></div>
<div class="score-mini team-b-bg"><span class="score-team-name">Team B</span><span id="pScoreB" class="score-value">0</span></div>
</div>
</div>
</div>
<div id="hostScreen" class="screen" style="display:none">
<div class="host-container">
<div class="host-header"><h1 class="host-title">🎬 Name That Tune! <span class="host-badge">HOST</span></h1></div>
<div class="host-grid">
<div class="host-left">
<div class="round-card">
<div class="round-badge large">Round <span id="hRoundNum">1</span> of <span id="hTotalRounds">10</span></div>
<div class="question-card">
<div class="question-answer" id="hAnswer">---</div>
<div class="question-hint" id="hHint"></div>
<a id="hYoutubeLink" class="youtube-link" href="#" target="_blank" style="display:none">▶ Play Film Score</a>
</div>
</div>
<div class="timer-card"><div class="timer-big" id="hTimer">30</div><div class="timer-label">seconds</div></div>
<div class="host-controls">
<button id="btnStart" class="ctrl-btn start-btn" onclick="hostStartRound()">Start Round</button>
<button id="btnStop" class="ctrl-btn stop-btn" onclick="hostStopRound()" style="display:none">Stop Round</button>
<button id="btnNext" class="ctrl-btn next-btn" onclick="hostNextRound()">Next Round</button>
<button id="btnReset" class="ctrl-btn reset-btn" onclick="hostResetRound()">Reset Round</button>
</div>
<div class="award-section">
<p class="award-label">Award point to:</p>
<div class="award-buttons">
<button class="award-btn team-a-bg" onclick="hostAwardPoint('Team A')">Team A</button>
<button class="award-btn team-b-bg" onclick="hostAwardPoint('Team B')">Team B</button>
</div>
</div>
</div>
<div class="host-right">
<div class="buzz-list-card"><h3 class="card-title">Buzz Order</h3><div id="hBuzzList" class="buzz-list"><p class="empty-text">No buzzes yet...</p></div></div>
<div class="players-card"><h3 class="card-title">Players (<span id="hPlayerCount">0</span>)</h3><div id="hPlayerList" class="player-list"></div></div>
<div class="scoreboard-card">
<h3 class="card-title">Scoreboard</h3>
<div class="scoreboard-big">
<div class="score-big team-a-bg"><span class="score-big-team">Team A</span><span id="hScoreA" class="score-big-value">0</span></div>
<div class="score-big-vs">VS</div>
<div class="score-big team-b-bg"><span class="score-big-team">Team B</span><span id="hScoreB" class="score-big-value">0</span></div>
</div>
</div>
</div>
</div>
<div class="host-footer"><button class="ctrl-btn danger-btn" onclick="hostResetGame()">Reset Entire Game</button></div>
</div>
</div>
<div id="gameOverScreen" class="screen" style="display:none">
<div class="gameover-container">
<h1 class="gameover-title">Game Over!</h1>
<div id="winnerAnnouncement" class="winner-text"></div>
<div class="scoreboard-final">
<div class="score-final team-a-bg"><span class="score-final-team">Team A</span><span id="fScoreA" class="score-final-value">0</span></div>
<div class="score-final-vs">VS</div>
<div class="score-final team-b-bg"><span class="score-final-team">Team B</span><span id="fScoreB" class="score-final-value">0</span></div>
</div>
<div id="confetti" class="confetti-container"></div>
</div>
</div>
<script>
var QUESTIONS = [
{round:1, title:"Jaws", hint:"You're gonna need a bigger boat", youtubeUrl:"https://youtube.com/clip/Ugkxbl7GSdxKsaPJXu3rAjssKOoUotAhz_eN?si=sDtemmz3MOng1Aci"},
{round:2, title:"The Good, the Bad and the Ugly", hint:"A three-way standoff in the desert", youtubeUrl:"https://youtube.com/clip/Ugkx-_6w7tyxCIy6mhJo89DbZygXU5h6ia5m?si=6xJ3-HmE9pTp2Ixe"},
{round:3, title:"Psycho", hint:"A shower you'll never forget", youtubeUrl:"https://youtube.com/clip/UgkxcL_f2GFuAPxyMxXpRRyR-D26cEuwBQJs?si=vUnt1l2VHuP_Za1j"},
{round:4, title:"Rocky", hint:"Gonna fly now", youtubeUrl:"https://youtube.com/clip/Ugkxm7dMcO8n6Huh2eEFIgEtfb9vVJNom8QC?si=NubSXjrUaBhE9zjx"},
{round:5, title:"E.T. the Extra-Terrestrial", hint:"Phone home", youtubeUrl:"https://youtube.com/clip/UgkxlKXtryvpus5aqVzQk4gOxHtRh0qJmqGn?si=jZkcZcNA9_Nc6_rp"},
{round:6, title:"The Pink Panther", hint:"A bumbling detective", youtubeUrl:"https://youtube.com/clip/Ugkx7Z7BXKkLpUO-b2lw4YpyGjbmytEERt2b?si=onUUQTnRah8GJJUQ"},
{round:7, title:"2001: A Space Odyssey", hint:"Open the pod bay doors", youtubeUrl:"https://youtube.com/clip/Ugkx99a8HgWx9KD_x9l9phQHz4qtQVeZe2K0?si=BQKSRwQEuIFUiHNC"},
{round:8, title:"Chariots of Fire", hint:"Running in slow motion on a beach", youtubeUrl:"https://youtube.com/clip/UgkxU_7sATU6sajPylUKoSHAvoxcm840pDcl?si=rlWRzWP80ra4Jnny"},
{round:9, title:"The Exorcist", hint:"Tubular Bells", youtubeUrl:"https://youtube.com/clip/UgkxCsCfIlo1ZKIrslwMGzvV48gIHwgEcM6O?si=FKlWd4NZK8o1aRzg"},
{round:10, title:"Cinema Paradiso", hint:"A boy who loved movies", youtubeUrl:"https://youtube.com/clip/Ugkxnagcgw1E_myc_Dm4m8aNBxEe57I-ZOhq?si=4IInD5TwRGOEPLOP"}
];
var SUPABASE_URL = 'https://kkzaugvaoytyoviwofsn.supabase.co';
var SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImtremF1Z3Zhb3l0eW92aXdvZnNuIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzM4ODgxNjEsImV4cCI6MjA4OTQ2NDE2MX0.bBoSWc_Ag0syFmPNyLujvNNM4AKUt-wG8ezK03jHMTI';
var sb = window.supabase.createClient(SUPABASE_URL, SUPABASE_KEY);
var channel = null;
var gameState = { currentRound:1, totalRounds:QUESTIONS.length, roundActive:false, timerEndTime:0, timerDuration:30, scores:{teamA:0,teamB:0}, buzzes:[], players:[] };
var playerName='', playerTeam='', hasBuzzedThisRound=false, isHostMode=false, timerInterval=null, lastKnownRound=0;
function setupChannel() {
channel = sb.channel('film-quiz-game', { config: { broadcast: { self: true } } });
channel.on('broadcast', {event:'game-state'}, function(p) {
var d = p.payload;
gameState.currentRound=d.currentRound; gameState.roundActive=d.roundActive;
gameState.timerEndTime=d.timerEndTime; gameState.timerDuration=d.timerDuration;
gameState.scores=d.scores; gameState.buzzes=d.buzzes; gameState.players=d.players;
if (isHostMode) updateHostUI(); else updatePlayerUI();
});
channel.on('broadcast', {event:'buzz'}, function(p) {
if (!isHostMode) return;
var d = p.payload;
for (var i=0; i<gameState.buzzes.length; i++) { if (gameState.buzzes[i].player===d.player) return; }
gameState.buzzes.push({player:d.player, team:d.team, position:gameState.buzzes.length+1, time:Date.now()});
broadcastState();
});
channel.on('broadcast', {event:'player-join'}, function(p) {
if (!isHostMode) return;
var d = p.payload;
for (var i=0; i<gameState.players.length; i++) {
if (gameState.players[i].name===d.name) { gameState.players[i].team=d.team; broadcastState(); return; }
}
gameState.players.push({name:d.name, team:d.team});
broadcastState();
});
channel.on('broadcast', {event:'buzz-confirm'}, function(p) {
if (isHostMode) return;
var d = p.payload;
if (d.player !== playerName) return;
document.getElementById('buzzButton').style.display='none';
document.getElementById('buzzResult').style.display='flex';
document.getElementById('buzzPosition').textContent='#'+d.position;
document.getElementById('buzzMessage').textContent='Position #'+d.position;
});
channel.subscribe(function(status) { console.log('Channel:', status); });
}
function broadcastState() {
if (!channel) return;
channel.send({type:'broadcast', event:'game-state', payload:{
currentRound:gameState.currentRound, roundActive:gameState.roundActive,
timerEndTime:gameState.timerEndTime, timerDuration:gameState.timerDuration,
scores:gameState.scores, buzzes:gameState.buzzes, players:gameState.players
}});
if (isHostMode) updateHostUI();
}
// Timer
function startTimer() { stopTimer(); timerInterval=setInterval(updateTimerDisplay,100); updateTimerDisplay(); }
function stopTimer() { if (timerInterval){clearInterval(timerInterval);timerInterval=null;} }
function getTimeRemaining() { if(!gameState.timerEndTime) return gameState.timerDuration; return Math.max(0,Math.ceil((gameState.timerEndTime-Date.now())/1000)); }
function getTimerColor(r) { var p=r/gameState.timerDuration; if(p>0.5)return'timer-green'; if(p>0.2)return'timer-yellow'; return'timer-red'; }
function updateTimerDisplay() {
var r = getTimeRemaining();
if (isHostMode) {
var el=document.getElementById('hTimer'); el.textContent=r;
el.className='timer-big '+(gameState.roundActive?getTimerColor(r):'');
if (r<=0 && gameState.roundActive) hostStopRound();
} else {
var d=document.getElementById('pTimerDisplay'), el=document.getElementById('pTimer');
if (gameState.roundActive) { d.style.display=''; el.textContent=r; d.className='timer-display '+getTimerColor(r); }
else { d.style.display='none'; }
}
}
// Init
function enterHostMode() {
isHostMode=true;
document.getElementById('joinScreen').style.display='none';
document.getElementById('hostScreen').style.display='flex';
setupChannel(); updateHostUI(); startTimer();
}
document.addEventListener('DOMContentLoaded', function() {
if (window.location.hash==='#host') { enterHostMode(); }
else { document.getElementById('joinScreen').style.display='flex'; document.getElementById('playerName').focus(); }
});
// Player join
function joinGame(team) {
var name=document.getElementById('playerName').value.trim();
if (!name) { document.getElementById('joinError').textContent='Please enter your name first!'; return; }
playerName=name; playerTeam=team;
document.getElementById('joinScreen').style.display='none';
document.getElementById('playerScreen').style.display='flex';
var badge=document.getElementById('pTeamBadge');
badge.textContent=team; badge.className='team-badge '+(team==='Team A'?'team-a-bg':'team-b-bg');
setupChannel(); startTimer();
setTimeout(function(){ channel.send({type:'broadcast',event:'player-join',payload:{name:playerName,team:playerTeam}}); },500);
}
// Player UI
function updatePlayerUI() {
if (gameState.currentRound>gameState.totalRounds){showGameOver();return;}
if (gameState.currentRound!==lastKnownRound) {
lastKnownRound=gameState.currentRound; hasBuzzedThisRound=false;
document.getElementById('buzzResult').style.display='none';
document.getElementById('buzzButton').style.display='';
}
document.getElementById('pRoundNum').textContent=gameState.currentRound;
document.getElementById('pTotalRounds').textContent=gameState.totalRounds;
var hintEl=document.getElementById('pHint'), q=QUESTIONS[gameState.currentRound-1];
if (gameState.roundActive&&q&&q.hint){hintEl.textContent='"'+q.hint+'"';hintEl.style.display='';} else {hintEl.style.display='none';}
var buzzBtn=document.getElementById('buzzButton'), statusEl=document.getElementById('pStatus');
if (gameState.roundActive&&!hasBuzzedThisRound){buzzBtn.disabled=false;buzzBtn.classList.add('active');statusEl.textContent='Name that tune!';statusEl.className='status-text active';}
else if (hasBuzzedThisRound){buzzBtn.disabled=true;buzzBtn.classList.remove('active');statusEl.textContent='You buzzed in!';statusEl.className='status-text buzzed';}
else {buzzBtn.disabled=true;buzzBtn.classList.remove('active');statusEl.textContent='Waiting for host to start the round...';statusEl.className='status-text waiting';}
document.getElementById('pScoreA').textContent=gameState.scores.teamA;
document.getElementById('pScoreB').textContent=gameState.scores.teamB;
}
// Buzz
function buzz() {
if (hasBuzzedThisRound||!gameState.roundActive) return;
hasBuzzedThisRound=true;
var buzzBtn=document.getElementById('buzzButton'); buzzBtn.disabled=true; buzzBtn.classList.remove('active');
channel.send({type:'broadcast',event:'buzz',payload:{player:playerName,team:playerTeam}});
document.getElementById('pStatus').textContent='Buzz sent!';
document.getElementById('pStatus').className='status-text buzzed';
}
// Host UI
function updateHostUI() {
if (gameState.currentRound>gameState.totalRounds){showGameOver();return;}
document.getElementById('hRoundNum').textContent=gameState.currentRound;
document.getElementById('hTotalRounds').textContent=gameState.totalRounds;
var q=QUESTIONS[gameState.currentRound-1];
if (q){
document.getElementById('hAnswer').textContent=q.title||'---';
document.getElementById('hHint').textContent=q.hint?'"'+q.hint+'"':'';
var yt=document.getElementById('hYoutubeLink');
if(q.youtubeUrl){yt.href=q.youtubeUrl;yt.style.display='';} else {yt.style.display='none';}
}
if(gameState.roundActive){document.getElementById('btnStart').style.display='none';document.getElementById('btnStop').style.display='';}
else{document.getElementById('btnStart').style.display='';document.getElementById('btnStop').style.display='none';}
var bl=document.getElementById('hBuzzList');
if(gameState.buzzes.length>0){
bl.innerHTML=gameState.buzzes.map(function(b){
var tc=b.team==='Team A'?'team-a-accent':'team-b-accent';
return '<div class="buzz-entry '+tc+'"><span class="buzz-position">#'+b.position+'</span><span class="buzz-player">'+esc(b.player)+'</span><span class="buzz-team-tag">'+b.team+'</span></div>';
}).join('');
gameState.buzzes.forEach(function(b){channel.send({type:'broadcast',event:'buzz-confirm',payload:{player:b.player,position:b.position}});});
} else { bl.innerHTML='<p class="empty-text">No buzzes yet...</p>'; }
document.getElementById('hPlayerCount').textContent=gameState.players.length;
var pl=document.getElementById('hPlayerList');
if(gameState.players.length>0){pl.innerHTML=gameState.players.map(function(p){var tc=p.team==='Team A'?'team-a-accent':'team-b-accent';return '<span class="player-tag '+tc+'">'+esc(p.name)+'</span>';}).join('');}
else{pl.innerHTML='<p class="empty-text">No players yet...</p>';}
document.getElementById('hScoreA').textContent=gameState.scores.teamA;
document.getElementById('hScoreB').textContent=gameState.scores.teamB;
}
function esc(t){var d=document.createElement('div');d.textContent=t;return d.innerHTML;}
// Host controls
function hostStartRound(){gameState.roundActive=true;gameState.timerEndTime=Date.now()+(gameState.timerDuration*1000);gameState.buzzes=[];broadcastState();}
function hostStopRound(){gameState.roundActive=false;gameState.timerEndTime=0;broadcastState();}
function hostNextRound(){if(gameState.currentRound>=gameState.totalRounds){gameState.currentRound=gameState.totalRounds+1;broadcastState();return;}gameState.currentRound++;gameState.roundActive=false;gameState.timerEndTime=0;gameState.buzzes=[];broadcastState();}
function hostResetRound(){if(!confirm('Reset this round?'))return;gameState.roundActive=false;gameState.timerEndTime=0;gameState.buzzes=[];broadcastState();}
function hostResetGame(){if(!confirm('Reset the ENTIRE game?'))return;gameState.currentRound=1;gameState.roundActive=false;gameState.timerEndTime=0;gameState.scores={teamA:0,teamB:0};gameState.buzzes=[];gameState.players=[];broadcastState();}
function hostAwardPoint(t){if(t==='Team A')gameState.scores.teamA++;else gameState.scores.teamB++;broadcastState();}
// Game over
function showGameOver(){
stopTimer();
document.getElementById('joinScreen').style.display='none';document.getElementById('playerScreen').style.display='none';
document.getElementById('hostScreen').style.display='none';document.getElementById('gameOverScreen').style.display='flex';
document.getElementById('fScoreA').textContent=gameState.scores.teamA;document.getElementById('fScoreB').textContent=gameState.scores.teamB;
var w=document.getElementById('winnerAnnouncement');
if(gameState.scores.teamA>gameState.scores.teamB){w.textContent='Team A Wins!';w.className='winner-text team-a-glow';}
else if(gameState.scores.teamB>gameState.scores.teamA){w.textContent='Team B Wins!';w.className='winner-text team-b-glow';}
else{w.textContent="It's a Tie!";w.className='winner-text';}
var c=document.getElementById('confetti'),cols=['#F59E0B','#06B6D4','#EF4444','#10B981','#8B5CF6','#F97316'];
for(var i=0;i<60;i++){var p=document.createElement('div');p.className='confetti-piece';p.style.left=Math.random()*100+'%';p.style.backgroundColor=cols[Math.floor(Math.random()*cols.length)];p.style.animationDelay=Math.random()*3+'s';p.style.animationDuration=(2+Math.random()*3)+'s';c.appendChild(p);}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment