Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Tempo Estimator</title> | |
| <!-- Bootstrap CSS --> | |
| <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> | |
| <!-- Google Fonts --> | |
| <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --bg-color-light: #f0f0f0; | |
| --text-color-light: #000; | |
| --bg-color-dark: #2c2c2c; | |
| --text-color-dark: #fff; | |
| } | |
| body { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| height: 100vh; | |
| margin: 0; | |
| font-family: 'Roboto', sans-serif; | |
| transition: background-color 0.3s, color 0.3s; | |
| text-align: center; | |
| } | |
| body.light-mode { | |
| background-color: var(--bg-color-light); | |
| color: var(--text-color-light); | |
| } | |
| body.dark-mode { | |
| background-color: var(--bg-color-dark); | |
| color: var(--text-color-dark); | |
| } | |
| .btn-custom { | |
| padding: 30px 60px; | |
| font-size: 36px; | |
| margin: 10px; | |
| border-radius: 50px; | |
| position: relative; | |
| } | |
| .btn-reset { | |
| padding: 15px 30px; | |
| font-size: 18px; | |
| margin: 10px; | |
| border-radius: 50px; | |
| position: relative; | |
| } | |
| #tempoDisplay { | |
| font-size: 36px; | |
| margin-bottom: 20px; | |
| } | |
| .toggle-button { | |
| display: flex; | |
| align-items: center; | |
| cursor: pointer; | |
| margin-top: 20px; | |
| } | |
| .toggle-button input { | |
| display: none; | |
| } | |
| .toggle-label { | |
| display: flex; | |
| align-items: center; | |
| background-color: #ccc; | |
| border-radius: 50px; | |
| padding: 5px; | |
| width: 40px; | |
| justify-content: space-between; | |
| position: relative; | |
| } | |
| .toggle-label .ball { | |
| background-color: #fff; | |
| border-radius: 50%; | |
| width: 20px; | |
| height: 20px; | |
| position: absolute; | |
| left: 0; | |
| transition: transform 0.3s; | |
| } | |
| input:checked + .toggle-label .ball { | |
| transform: translateX(20px); | |
| } | |
| .sparkle { | |
| position: absolute; | |
| width: 20px; | |
| height: 20px; | |
| background: orange; | |
| border-radius: 50%; | |
| animation: sparkle 0.5s ease-out; | |
| } | |
| @keyframes sparkle { | |
| from { | |
| transform: scale(0); | |
| opacity: 1; | |
| } | |
| to { | |
| transform: scale(1.5); | |
| opacity: 0; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="light-mode"> | |
| <div id="tempoDisplay">-- BPM</div> | |
| <button id="tapButton" class="btn btn-primary btn-custom">Tap</button> | |
| <button id="resetButton" class="btn btn-secondary btn-reset">Reset</button> | |
| <div class="toggle-button"> | |
| <input type="checkbox" id="modeToggle"> | |
| <label for="modeToggle" class="toggle-label"> | |
| <div class="ball"></div> | |
| </label> | |
| </div> | |
| <script> | |
| let tapTimes = []; | |
| document.getElementById('tapButton').addEventListener('click', (e) => { | |
| const currentTime = new Date().getTime(); | |
| tapTimes.push(currentTime); | |
| if (tapTimes.length > 1) { | |
| const timeIntervals = []; | |
| for (let i = 1; i < tapTimes.length; i++) { | |
| timeIntervals.push(tapTimes[i] - tapTimes[i - 1]); | |
| } | |
| const averageInterval = timeIntervals.reduce((a, b) => a + b) / timeIntervals.length; | |
| const tempo = Math.round(60000 / averageInterval); | |
| document.getElementById('tempoDisplay').innerText = `${tempo} BPM`; | |
| } | |
| if (tapTimes.length > 10) { | |
| tapTimes.shift(); | |
| } | |
| // Sparkle effect | |
| const sparkle = document.createElement('div'); | |
| sparkle.className = 'sparkle'; | |
| sparkle.style.left = `${e.clientX - e.target.offsetLeft - 10}px`; | |
| sparkle.style.top = `${e.clientY - e.target.offsetTop - 10}px`; | |
| e.target.appendChild(sparkle); | |
| setTimeout(() => { | |
| sparkle.remove(); | |
| }, 500); | |
| }); | |
| document.getElementById('resetButton').addEventListener('click', () => { | |
| tapTimes = []; | |
| document.getElementById('tempoDisplay').innerText = '-- BPM'; | |
| }); | |
| document.getElementById('modeToggle').addEventListener('change', () => { | |
| const body = document.body; | |
| if (body.classList.contains('light-mode')) { | |
| body.classList.remove('light-mode'); | |
| body.classList.add('dark-mode'); | |
| } else { | |
| body.classList.remove('dark-mode'); | |
| body.classList.add('light-mode'); | |
| } | |
| }); | |
| document.addEventListener('keydown', (event) => { | |
| if (event.key === ' ') { | |
| document.getElementById('tapButton').click(); | |
| } | |
| }); | |
| </script> | |
| <!-- Bootstrap JS and dependencies --> | |
| <script src="https://code.jquery.com/jquery-3.5.1.s | |