Liveloop

Uma timeline interativa para redes sociais. Cada post é um app minúsculo que você pode jogar, salvar e remixar.

Produto

  • Feed
  • Jogos
  • Criar
  • Plugin do Claude Code
  • Blog

Jurídico

  • Política de Privacidade
  • Termos de Serviço
  • Política de Cookies
  • DMCA

Projeto

  • Templates
© 2026 Liveloop. Todos os direitos reservados.
LiveloopHistórico de versões

v1Atual

@joaobarrosfranca · 8/12/2026, 7:12:08 AM

Versão inicial — todas as linhas são novas.

+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Signal Hunt</title>
+ <style>
+ * { box-sizing: border-box; }
+ html, body {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: linear-gradient(135deg, #FFF8F0 0%, #FFEBD8 100%);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ }
+
+ .container {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+ padding: 16px;
+ gap: 12px;
+ }
+
+ .header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 8px;
+ font-size: 14px;
+ font-weight: 600;
+ color: #5a3a5a;
+ }
+
+ .stat {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ }
+
+ .stat-label {
+ font-size: 12px;
+ opacity: 0.7;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ }
+
+ .stat-value {
+ font-size: 20px;
+ font-weight: 700;
+ color: #d97b3a;
+ }
+
+ .timer-ring {
+ position: relative;
+ width: 60px;
+ height: 60px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .timer-svg {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ transform: rotate(-90deg);
+ }
+
+ .timer-circle-bg {
+ fill: none;
+ stroke: #f0d9cc;
+ stroke-width: 4;
+ }
+
+ .timer-circle-fg {
+ fill: none;
+ stroke: #d97b3a;
+ stroke-width: 4;
+ stroke-linecap: round;
+ transition: stroke-dashoffset 0.05s linear;
+ }
+
+ .timer-text {
+ position: relative;
+ font-size: 18px;
+ font-weight: 700;
+ color: #5a3a5a;
+ }
+
+ .game-area {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 0;
+ }
+
+ .grid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 8px;
+ aspect-ratio: 1;
+ width: 100%;
+ max-width: 320px;
+ }
+
+ .cell {
+ aspect-ratio: 1;
+ border: none;
+ border-radius: 16px;
+ font-size: 48px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ background: white;
+ box-shadow: 0 4px 12px rgba(90, 58, 90, 0.12);
+ transition: all 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
+ user-select: none;
+ -webkit-user-select: none;
+ -webkit-tap-highlight-color: transparent;
+ }
+
+ .cell:active {
+ transform: scale(0.92);
+ }
+
+ .cell.correct {
+ animation: correctPulse 0.4s ease-out;
+ }
+
+ .cell.wrong {
+ animation: wrongShake 0.4s ease-out;
+ }
+
+ @keyframes correctPulse {
+ 0% { transform: scale(1); background: white; }
+ 50% { transform: scale(1.08); background: #d4f4dd; box-shadow: 0 8px 24px rgba(76, 175, 80, 0.4); }
+ 100% { transform: scale(1); background: white; }
+ }
+
+ @keyframes wrongShake {
+ 0%, 100% { transform: translateX(0); }
+ 25% { transform: translateX(-8px); }
+ 75% { transform: translateX(8px); }
+ }
+
+ .footer {
+ display: flex;
+ gap: 8px;
+ justify-content: center;
+ padding: 8px 0;
+ }
+
+ .btn {
+ padding: 12px 24px;
+ border: none;
+ border-radius: 12px;
+ font-size: 16px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ user-select: none;
+ -webkit-user-select: none;
+ -webkit-tap-highlight-color: transparent;
+ }
+
+ .btn-replay {
+ background: linear-gradient(135deg, #d97b3a 0%, #e8965a 100%);
+ color: white;
+ padding: 14px 32px;
+ font-size: 17px;
+ box-shadow: 0 4px 12px rgba(217, 123, 58, 0.3);
+ }
+
+ .btn-replay:active {
+ transform: scale(0.96);
+ box-shadow: 0 2px 6px rgba(217, 123, 58, 0.3);
+ }
+
+ .modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+ align-items: center;
+ justify-content: center;
+ padding: 16px;
+ }
+
+ .modal.active {
+ display: flex;
+ }
+
+ .modal-content {
+ background: linear-gradient(135deg, #FFF8F0 0%, #FFEBD8 100%);
+ border-radius: 24px;
+ padding: 40px 32px;
+ text-align: center;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+ max-width: 320px;
+ }
+
+ .modal-title {
+ font-size: 28px;
+ font-weight: 700;
+ color: #5a3a5a;
+ margin: 0 0 8px 0;
+ }
+
+ .modal-subtitle {
+ font-size: 14px;
+ color: #8b5a8b;
+ margin: 0 0 24px 0;
+ opacity: 0.8;
+ }
+
+ .modal-stats {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ margin-bottom: 32px;
+ }
+
+ .modal-stat-item {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ }
+
+ .modal-stat-label {
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ color: #8b5a8b;
+ font-weight: 600;
+ }
+
+ .modal-stat-value {
+ font-size: 36px;
+ font-weight: 700;
+ color: #d97b3a;
+ }
+
+ .share-prompt {
+ font-size: 13px;
+ color: #5a3a5a;
+ margin-bottom: 24px;
+ line-height: 1.4;
+ }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <div class="header">
+ <div class="stat">
+ <div class="stat-label">Score</div>
+ <div class="stat-value" id="scoreDisplay">0</div>
+ </div>
+ <div class="timer-ring">
+ <svg class="timer-svg" viewBox="0 0 60 60">
+ <circle class="timer-circle-bg" cx="30" cy="30" r="26"></circle>
+ <circle class="timer-circle-fg" id="timerCircle" cx="30" cy="30" r="26"
+ style="stroke-dasharray: 163.36; stroke-dashoffset: 0;"></circle>
+ </svg>
+ <div class="timer-text" id="timerText">10</div>
+ </div>
+ <div class="stat">
+ <div class="stat-label">Streak</div>
+ <div class="stat-value" id="streakDisplay">0</div>
+ </div>
+ </div>
+
+ <div class="game-area">
+ <div class="grid" id="grid"></div>
+ </div>
+
+ <div class="footer">
+ <button class="btn btn-replay" id="replayBtn">Play Again</button>
+ </div>
+ </div>
+
+ <div class="modal" id="gameOverModal">
+ <div class="modal-content">
+ <div class="modal-title" id="modalTitle">Game Over!</div>
+ <div class="modal-subtitle">Final Results</div>
+ <div class="modal-stats">
+ <div class="modal-stat-item">
+ <div class="modal-stat-label">Final Score</div>
+ <div class="modal-stat-value" id="modalScore">0</div>
+ </div>
+ <div class="modal-stat-item">
+ <div class="modal-stat-label">Best Streak</div>
+ <div class="modal-stat-value" id="modalStreak">0</div>
+ </div>
+ </div>
+ <div class="share-prompt">Tap "Play Again" for another round! 🎯</div>
+ <button class="btn btn-replay" id="modalReplayBtn">Play Again</button>
+ </div>
+ </div>
+
+ <script>
+ // Symbols for the grid
+ const SYMBOLS = ['🎯', '⭐', '🔥', '💎', '🎪', '🌙', '⚡', '🎨'];
+
+ // Game state
+ let gameState = {
+ score: 0,
+ streak: 0,
+ maxStreak: 0,
+ timeLeft: 10,
+ gameActive: true,
+ board: null,
+ targetIndex: null,
+ answered: false,
+ };
+
+ let timerInterval = null;
+ let audioCtx = null;
+
+ // Initialize grid
+ const gridEl = document.getElementById('grid');
+ const replayBtn = document.getElementById('replayBtn');
+ const modalReplayBtn = document.getElementById('modalReplayBtn');
+ const gameOverModal = document.getElementById('gameOverModal');
+
+ function generateBoard() {
+ const mainSymbol = SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)];
+ let oddSymbol = SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)];
+ while (oddSymbol === mainSymbol) {
+ oddSymbol = SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)];
+ }
+
+ const targetIndex = Math.floor(Math.random() * 16);
+ const board = Array(16).fill(mainSymbol);
+ board[targetIndex] = oddSymbol;
+
+ return { board, targetIndex, mainSymbol, oddSymbol };
+ }
+
+ function renderBoard() {
+ gridEl.innerHTML = '';
+ gameState.board.board.forEach((symbol, index) => {
+ const cell = document.createElement('button');
+ cell.className = 'cell';
+ cell.textContent = symbol;
+ cell.addEventListener('click', () => handleCellClick(index));
+ gridEl.appendChild(cell);
+ });
+ }
+
+ function handleCellClick(index) {
+ if (!gameState.gameActive || gameState.answered) return;
+
+ gameState.answered = true;
+ const cell = document.querySelector('.grid').children[index];
+
+ if (index === gameState.board.targetIndex) {
+ // Correct!
+ playSound(800, 200, 0.3);
+ cell.classList.add('correct');
+ gameState.score += 10;
+ gameState.streak += 1;
+ gameState.maxStreak = Math.max(gameState.maxStreak, gameState.streak);
+ } else {
+ // Wrong!
+ playSound(300, 300, 0.2);
+ cell.classList.add('wrong');
+ gameState.streak = 0;
+ }
+
+ updateDisplay();
+ setTimeout(nextRound, 500);
+ }
+
+ function nextRound() {
+ if (!gameState.gameActive) return;
+ gameState.board = generateBoard();
+ gameState.answered = false;
+ renderBoard();
+ }
+
+ function updateDisplay() {
+ document.getElementById('scoreDisplay').textContent = gameState.score;
+ document.getElementById('streakDisplay').textContent = gameState.streak;
+ }
+
+ function startTimer() {
+ gameState.timeLeft = 10;
+ if (timerInterval) clearInterval(timerInterval);
+
+ updateTimerDisplay();
+ timerInterval = setInterval(() => {
+ gameState.timeLeft -= 0.1;
+ updateTimerDisplay();
+
+ if (gameState.timeLeft <= 0) {
+ endGame();
+ }
+ }, 100);
+ }
+
+ function updateTimerDisplay() {
+ const displayTime = Math.max(0, Math.ceil(gameState.timeLeft));
+ document.getElementById('timerText').textContent = displayTime;
+
+ const circumference = 2 * Math.PI * 26;
+ const offset = circumference * (1 - gameState.timeLeft / 10);
+ document.getElementById('timerCircle').style.strokeDashoffset = offset;
+ }
+
+ function endGame() {
+ gameState.gameActive = false;
+ if (timerInterval) clearInterval(timerInterval);
+
+ // Show modal after a brief delay
+ setTimeout(() => {
+ document.getElementById('modalScore').textContent = gameState.score;
+ document.getElementById('modalStreak').textContent = gameState.maxStreak;
+ gameOverModal.classList.add('active');
+ }, 200);
+ }
+
+ function resetGame() {
+ gameState.score = 0;
+ gameState.streak = 0;
+ gameState.maxStreak = 0;
+ gameState.timeLeft = 10;
+ gameState.gameActive = true;
+ gameState.answered = false;
+ gameOverModal.classList.remove('active');
+ updateDisplay();
+ gameState.board = generateBoard();
+ renderBoard();
+ startTimer();
+ }
+
+ function playSound(freq, duration, volume) {
+ try {
+ if (!audioCtx) {
+ audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ }
+ if (audioCtx.state === 'suspended') audioCtx.resume();
+
+ const now = audioCtx.currentTime;
+ const osc = audioCtx.createOscillator();
+ const gain = audioCtx.createGain();
+
+ osc.connect(gain);
+ gain.connect(audioCtx.destination);
+
+ osc.frequency.value = freq;
+ osc.type = 'sine';
+
+ gain.gain.setValueAtTime(volume, now);
+ gain.gain.exponentialRampToValueAtTime(0.01, now + duration / 1000);
+
+ osc.start(now);
+ osc.stop(now + duration / 1000);
+ } catch (e) {
+ // Audio unavailable
+ }
+ }
+
+ // Event listeners
+ replayBtn.addEventListener('click', resetGame);
+ modalReplayBtn.addEventListener('click', resetGame);
+
+ // Visibility handling
+ if (window.liveloop && window.liveloop.onVisibility) {
+ window.liveloop.onVisibility(function(visible) {
+ if (!visible && timerInterval) {
+ clearInterval(timerInterval);
+ } else if (visible && gameState.gameActive) {
+ startTimer();
+ }
+ });
+ }
+
+ // Start the game
+ resetGame();
+ </script>
+</body>
+</html>

v1Atual

@joaobarrosfranca · 8/12/2026, 7:12:08 AM

Versão inicial — todas as linhas são novas.

+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Signal Hunt</title>
+ <style>
+ * { box-sizing: border-box; }
+ html, body {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background: linear-gradient(135deg, #FFF8F0 0%, #FFEBD8 100%);
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ }
+
+ .container {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+ padding: 16px;
+ gap: 12px;
+ }
+
+ .header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 8px;
+ font-size: 14px;
+ font-weight: 600;
+ color: #5a3a5a;
+ }
+
+ .stat {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ }
+
+ .stat-label {
+ font-size: 12px;
+ opacity: 0.7;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ }
+
+ .stat-value {
+ font-size: 20px;
+ font-weight: 700;
+ color: #d97b3a;
+ }
+
+ .timer-ring {
+ position: relative;
+ width: 60px;
+ height: 60px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .timer-svg {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ transform: rotate(-90deg);
+ }
+
+ .timer-circle-bg {
+ fill: none;
+ stroke: #f0d9cc;
+ stroke-width: 4;
+ }
+
+ .timer-circle-fg {
+ fill: none;
+ stroke: #d97b3a;
+ stroke-width: 4;
+ stroke-linecap: round;
+ transition: stroke-dashoffset 0.05s linear;
+ }
+
+ .timer-text {
+ position: relative;
+ font-size: 18px;
+ font-weight: 700;
+ color: #5a3a5a;
+ }
+
+ .game-area {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 0;
+ }
+
+ .grid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 8px;
+ aspect-ratio: 1;
+ width: 100%;
+ max-width: 320px;
+ }
+
+ .cell {
+ aspect-ratio: 1;
+ border: none;
+ border-radius: 16px;
+ font-size: 48px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ background: white;
+ box-shadow: 0 4px 12px rgba(90, 58, 90, 0.12);
+ transition: all 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
+ user-select: none;
+ -webkit-user-select: none;
+ -webkit-tap-highlight-color: transparent;
+ }
+
+ .cell:active {
+ transform: scale(0.92);
+ }
+
+ .cell.correct {
+ animation: correctPulse 0.4s ease-out;
+ }
+
+ .cell.wrong {
+ animation: wrongShake 0.4s ease-out;
+ }
+
+ @keyframes correctPulse {
+ 0% { transform: scale(1); background: white; }
+ 50% { transform: scale(1.08); background: #d4f4dd; box-shadow: 0 8px 24px rgba(76, 175, 80, 0.4); }
+ 100% { transform: scale(1); background: white; }
+ }
+
+ @keyframes wrongShake {
+ 0%, 100% { transform: translateX(0); }
+ 25% { transform: translateX(-8px); }
+ 75% { transform: translateX(8px); }
+ }
+
+ .footer {
+ display: flex;
+ gap: 8px;
+ justify-content: center;
+ padding: 8px 0;
+ }
+
+ .btn {
+ padding: 12px 24px;
+ border: none;
+ border-radius: 12px;
+ font-size: 16px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ user-select: none;
+ -webkit-user-select: none;
+ -webkit-tap-highlight-color: transparent;
+ }
+
+ .btn-replay {
+ background: linear-gradient(135deg, #d97b3a 0%, #e8965a 100%);
+ color: white;
+ padding: 14px 32px;
+ font-size: 17px;
+ box-shadow: 0 4px 12px rgba(217, 123, 58, 0.3);
+ }
+
+ .btn-replay:active {
+ transform: scale(0.96);
+ box-shadow: 0 2px 6px rgba(217, 123, 58, 0.3);
+ }
+
+ .modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+ align-items: center;
+ justify-content: center;
+ padding: 16px;
+ }
+
+ .modal.active {
+ display: flex;
+ }
+
+ .modal-content {
+ background: linear-gradient(135deg, #FFF8F0 0%, #FFEBD8 100%);
+ border-radius: 24px;
+ padding: 40px 32px;
+ text-align: center;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+ max-width: 320px;
+ }
+
+ .modal-title {
+ font-size: 28px;
+ font-weight: 700;
+ color: #5a3a5a;
+ margin: 0 0 8px 0;
+ }
+
+ .modal-subtitle {
+ font-size: 14px;
+ color: #8b5a8b;
+ margin: 0 0 24px 0;
+ opacity: 0.8;
+ }
+
+ .modal-stats {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ margin-bottom: 32px;
+ }
+
+ .modal-stat-item {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ }
+
+ .modal-stat-label {
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ color: #8b5a8b;
+ font-weight: 600;
+ }
+
+ .modal-stat-value {
+ font-size: 36px;
+ font-weight: 700;
+ color: #d97b3a;
+ }
+
+ .share-prompt {
+ font-size: 13px;
+ color: #5a3a5a;
+ margin-bottom: 24px;
+ line-height: 1.4;
+ }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <div class="header">
+ <div class="stat">
+ <div class="stat-label">Score</div>
+ <div class="stat-value" id="scoreDisplay">0</div>
+ </div>
+ <div class="timer-ring">
+ <svg class="timer-svg" viewBox="0 0 60 60">
+ <circle class="timer-circle-bg" cx="30" cy="30" r="26"></circle>
+ <circle class="timer-circle-fg" id="timerCircle" cx="30" cy="30" r="26"
+ style="stroke-dasharray: 163.36; stroke-dashoffset: 0;"></circle>
+ </svg>
+ <div class="timer-text" id="timerText">10</div>
+ </div>
+ <div class="stat">
+ <div class="stat-label">Streak</div>
+ <div class="stat-value" id="streakDisplay">0</div>
+ </div>
+ </div>
+
+ <div class="game-area">
+ <div class="grid" id="grid"></div>
+ </div>
+
+ <div class="footer">
+ <button class="btn btn-replay" id="replayBtn">Play Again</button>
+ </div>
+ </div>
+
+ <div class="modal" id="gameOverModal">
+ <div class="modal-content">
+ <div class="modal-title" id="modalTitle">Game Over!</div>
+ <div class="modal-subtitle">Final Results</div>
+ <div class="modal-stats">
+ <div class="modal-stat-item">
+ <div class="modal-stat-label">Final Score</div>
+ <div class="modal-stat-value" id="modalScore">0</div>
+ </div>
+ <div class="modal-stat-item">
+ <div class="modal-stat-label">Best Streak</div>
+ <div class="modal-stat-value" id="modalStreak">0</div>
+ </div>
+ </div>
+ <div class="share-prompt">Tap "Play Again" for another round! 🎯</div>
+ <button class="btn btn-replay" id="modalReplayBtn">Play Again</button>
+ </div>
+ </div>
+
+ <script>
+ // Symbols for the grid
+ const SYMBOLS = ['🎯', '⭐', '🔥', '💎', '🎪', '🌙', '⚡', '🎨'];
+
+ // Game state
+ let gameState = {
+ score: 0,
+ streak: 0,
+ maxStreak: 0,
+ timeLeft: 10,
+ gameActive: true,
+ board: null,
+ targetIndex: null,
+ answered: false,
+ };
+
+ let timerInterval = null;
+ let audioCtx = null;
+
+ // Initialize grid
+ const gridEl = document.getElementById('grid');
+ const replayBtn = document.getElementById('replayBtn');
+ const modalReplayBtn = document.getElementById('modalReplayBtn');
+ const gameOverModal = document.getElementById('gameOverModal');
+
+ function generateBoard() {
+ const mainSymbol = SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)];
+ let oddSymbol = SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)];
+ while (oddSymbol === mainSymbol) {
+ oddSymbol = SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)];
+ }
+
+ const targetIndex = Math.floor(Math.random() * 16);
+ const board = Array(16).fill(mainSymbol);
+ board[targetIndex] = oddSymbol;
+
+ return { board, targetIndex, mainSymbol, oddSymbol };
+ }
+
+ function renderBoard() {
+ gridEl.innerHTML = '';
+ gameState.board.board.forEach((symbol, index) => {
+ const cell = document.createElement('button');
+ cell.className = 'cell';
+ cell.textContent = symbol;
+ cell.addEventListener('click', () => handleCellClick(index));
+ gridEl.appendChild(cell);
+ });
+ }
+
+ function handleCellClick(index) {
+ if (!gameState.gameActive || gameState.answered) return;
+
+ gameState.answered = true;
+ const cell = document.querySelector('.grid').children[index];
+
+ if (index === gameState.board.targetIndex) {
+ // Correct!
+ playSound(800, 200, 0.3);
+ cell.classList.add('correct');
+ gameState.score += 10;
+ gameState.streak += 1;
+ gameState.maxStreak = Math.max(gameState.maxStreak, gameState.streak);
+ } else {
+ // Wrong!
+ playSound(300, 300, 0.2);
+ cell.classList.add('wrong');
+ gameState.streak = 0;
+ }
+
+ updateDisplay();
+ setTimeout(nextRound, 500);
+ }
+
+ function nextRound() {
+ if (!gameState.gameActive) return;
+ gameState.board = generateBoard();
+ gameState.answered = false;
+ renderBoard();
+ }
+
+ function updateDisplay() {
+ document.getElementById('scoreDisplay').textContent = gameState.score;
+ document.getElementById('streakDisplay').textContent = gameState.streak;
+ }
+
+ function startTimer() {
+ gameState.timeLeft = 10;
+ if (timerInterval) clearInterval(timerInterval);
+
+ updateTimerDisplay();
+ timerInterval = setInterval(() => {
+ gameState.timeLeft -= 0.1;
+ updateTimerDisplay();
+
+ if (gameState.timeLeft <= 0) {
+ endGame();
+ }
+ }, 100);
+ }
+
+ function updateTimerDisplay() {
+ const displayTime = Math.max(0, Math.ceil(gameState.timeLeft));
+ document.getElementById('timerText').textContent = displayTime;
+
+ const circumference = 2 * Math.PI * 26;
+ const offset = circumference * (1 - gameState.timeLeft / 10);
+ document.getElementById('timerCircle').style.strokeDashoffset = offset;
+ }
+
+ function endGame() {
+ gameState.gameActive = false;
+ if (timerInterval) clearInterval(timerInterval);
+
+ // Show modal after a brief delay
+ setTimeout(() => {
+ document.getElementById('modalScore').textContent = gameState.score;
+ document.getElementById('modalStreak').textContent = gameState.maxStreak;
+ gameOverModal.classList.add('active');
+ }, 200);
+ }
+
+ function resetGame() {
+ gameState.score = 0;
+ gameState.streak = 0;
+ gameState.maxStreak = 0;
+ gameState.timeLeft = 10;
+ gameState.gameActive = true;
+ gameState.answered = false;
+ gameOverModal.classList.remove('active');
+ updateDisplay();
+ gameState.board = generateBoard();
+ renderBoard();
+ startTimer();
+ }
+
+ function playSound(freq, duration, volume) {
+ try {
+ if (!audioCtx) {
+ audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ }
+ if (audioCtx.state === 'suspended') audioCtx.resume();
+
+ const now = audioCtx.currentTime;
+ const osc = audioCtx.createOscillator();
+ const gain = audioCtx.createGain();
+
+ osc.connect(gain);
+ gain.connect(audioCtx.destination);
+
+ osc.frequency.value = freq;
+ osc.type = 'sine';
+
+ gain.gain.setValueAtTime(volume, now);
+ gain.gain.exponentialRampToValueAtTime(0.01, now + duration / 1000);
+
+ osc.start(now);
+ osc.stop(now + duration / 1000);
+ } catch (e) {
+ // Audio unavailable
+ }
+ }
+
+ // Event listeners
+ replayBtn.addEventListener('click', resetGame);
+ modalReplayBtn.addEventListener('click', resetGame);
+
+ // Visibility handling
+ if (window.liveloop && window.liveloop.onVisibility) {
+ window.liveloop.onVisibility(function(visible) {
+ if (!visible && timerInterval) {
+ clearInterval(timerInterval);
+ } else if (visible && gameState.gameActive) {
+ startTimer();
+ }
+ });
+ }
+
+ // Start the game
+ resetGame();
+ </script>
+</body>
+</html>
← Histórico de versões