Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Karaoke Master</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&family=Roboto:wght@400;700&family=Montserrat:wght@400;700&family=Dancing+Script:wght@700&family=Open+Sans:wght@400;700&display=swap" rel="stylesheet"> | |
| <style> | |
| .lyrics-container { | |
| height: 400px; | |
| overflow-y: auto; | |
| scroll-behavior: smooth; | |
| } | |
| .highlight { | |
| transition: color 0.3s ease; | |
| } | |
| .progress-bar { | |
| transition: width 0.1s linear; | |
| } | |
| .word { | |
| transition: all 0.3s ease; | |
| display: inline-block; | |
| white-space: pre-wrap; | |
| } | |
| .word.active { | |
| transform: scale(1.1); | |
| } | |
| .font-poppins { | |
| font-family: 'Poppins', sans-serif; | |
| } | |
| .font-roboto { | |
| font-family: 'Roboto', sans-serif; | |
| } | |
| .font-montserrat { | |
| font-family: 'Montserrat', sans-serif; | |
| } | |
| .font-dancing { | |
| font-family: 'Dancing Script', cursive; | |
| } | |
| .font-opensans { | |
| font-family: 'Open Sans', sans-serif; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <h1 class="text-4xl font-bold text-center mb-8 text-purple-400"> | |
| <i class="fas fa-microphone-alt mr-2"></i> Karaoke Master | |
| </h1> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
| <!-- Controls Panel --> | |
| <div class="bg-gray-800 p-6 rounded-xl shadow-lg"> | |
| <h2 class="text-2xl font-semibold mb-4 text-purple-300"> | |
| <i class="fas fa-sliders-h mr-2"></i> Controls | |
| </h2> | |
| <div class="space-y-6"> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Highlight Speed</label> | |
| <input type="range" id="speed" min="50" max="500" value="200" class="w-full accent-purple-500"> | |
| <div class="flex justify-between text-xs text-gray-400"> | |
| <span>Slow</span> | |
| <span>Fast</span> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Highlight Color</label> | |
| <div class="flex space-x-2"> | |
| <button data-color="#f472b6" class="w-8 h-8 rounded-full bg-pink-400 border-2 border-transparent hover:border-white focus:border-white" onclick="setHighlightColor('#f472b6')"></button> | |
| <button data-color="#60a5fa" class="w-8 h-8 rounded-full bg-blue-400 border-2 border-transparent hover:border-white focus:border-white" onclick="setHighlightColor('#60a5fa')"></button> | |
| <button data-color="#34d399" class="w-8 h-8 rounded-full bg-green-400 border-2 border-transparent hover:border-white focus:border-white" onclick="setHighlightColor('#34d399')"></button> | |
| <button data-color="#fbbf24" class="w-8 h-8 rounded-full bg-yellow-400 border-2 border-transparent hover:border-white focus:border-white" onclick="setHighlightColor('#fbbf24')"></button> | |
| <button data-color="#f87171" class="w-8 h-8 rounded-full bg-red-400 border-2 border-transparent hover:border-white focus:border-white" onclick="setHighlightColor('#f87171')"></button> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Text Size</label> | |
| <select id="textSize" class="w-full bg-gray-700 border border-gray-600 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
| <option value="text-sm">Small</option> | |
| <option value="text-base" selected>Medium</option> | |
| <option value="text-lg">Large</option> | |
| <option value="text-xl">Extra Large</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Font Family</label> | |
| <select id="fontFamily" class="w-full bg-gray-700 border border-gray-600 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-purple-500"> | |
| <option value="font-poppins">Poppins</option> | |
| <option value="font-roboto" selected>Roboto</option> | |
| <option value="font-montserrat">Montserrat</option> | |
| <option value="font-dancing">Dancing Script</option> | |
| <option value="font-opensans">Open Sans</option> | |
| </select> | |
| </div> | |
| <div class="pt-4 border-t border-gray-700"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <span class="text-sm font-medium">Time Elapsed:</span> | |
| <span id="timeCounter" class="font-mono bg-gray-700 px-2 py-1 rounded">00:00.00</span> | |
| </div> | |
| <button id="playBtn" class="w-full bg-purple-600 hover:bg-purple-700 text-white font-bold py-3 px-4 rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-play mr-2"></i> Start Karaoke | |
| </button> | |
| <button id="pauseBtn" class="w-full mt-2 bg-gray-600 hover:bg-gray-700 text-white font-bold py-3 px-4 rounded-lg flex items-center justify-center hidden"> | |
| <i class="fas fa-pause mr-2"></i> Pause | |
| </button> | |
| <button id="stopBtn" class="w-full mt-2 bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-4 rounded-lg flex items-center justify-center"> | |
| <i class="fas fa-stop mr-2"></i> Stop | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Lyrics Display --> | |
| <div class="lg:col-span-2"> | |
| <div class="bg-gray-800 rounded-xl shadow-lg overflow-hidden"> | |
| <div class="p-4 bg-gray-700 flex justify-between items-center"> | |
| <h2 class="text-xl font-semibold text-purple-300"> | |
| <i class="fas fa-music mr-2"></i> Lyrics Display | |
| </h2> | |
| <div class="w-1/2 bg-gray-600 rounded-full h-2.5"> | |
| <div id="progressBar" class="progress-bar h-2.5 rounded-full bg-purple-500" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| <div id="lyricsDisplay" class="lyrics-container p-6 text-center text-base leading-relaxed font-roboto"> | |
| <p class="text-gray-400 italic">Enter lyrics below to get started...</p> | |
| </div> | |
| </div> | |
| <!-- Lyrics Input --> | |
| <div class="mt-6 bg-gray-800 rounded-xl shadow-lg overflow-hidden"> | |
| <div class="p-4 bg-gray-700"> | |
| <h2 class="text-xl font-semibold text-purple-300"> | |
| <i class="fas fa-edit mr-2"></i> Lyrics Input | |
| </h2> | |
| </div> | |
| <div class="p-4"> | |
| <textarea id="lyricsInput" rows="8" class="w-full bg-gray-700 border border-gray-600 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-purple-500" placeholder="Enter your lyrics here..."></textarea> | |
| <div class="mt-4 flex justify-between"> | |
| <button id="loadSampleBtn" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded-lg"> | |
| <i class="fas fa-lightbulb mr-2"></i> Load Sample | |
| </button> | |
| <button id="updateLyricsBtn" class="bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded-lg"> | |
| <i class="fas fa-sync-alt mr-2"></i> Update Lyrics | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // DOM Elements | |
| const lyricsInput = document.getElementById('lyricsInput'); | |
| const lyricsDisplay = document.getElementById('lyricsDisplay'); | |
| const playBtn = document.getElementById('playBtn'); | |
| const pauseBtn = document.getElementById('pauseBtn'); | |
| const stopBtn = document.getElementById('stopBtn'); | |
| const updateLyricsBtn = document.getElementById('updateLyricsBtn'); | |
| const loadSampleBtn = document.getElementById('loadSampleBtn'); | |
| const speedControl = document.getElementById('speed'); | |
| const textSizeSelect = document.getElementById('textSize'); | |
| const fontFamilySelect = document.getElementById('fontFamily'); | |
| const progressBar = document.getElementById('progressBar'); | |
| const timeCounter = document.getElementById('timeCounter'); | |
| // State variables | |
| let words = []; | |
| let currentWordIndex = 0; | |
| let highlightInterval; | |
| let isPlaying = false; | |
| let highlightColor = '#f472b6'; // Default pink color | |
| let startTime = 0; | |
| let elapsedTime = 0; | |
| let timeInterval; | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Event listeners | |
| playBtn.addEventListener('click', startKaraoke); | |
| pauseBtn.addEventListener('click', pauseKaraoke); | |
| stopBtn.addEventListener('click', stopKaraoke); | |
| updateLyricsBtn.addEventListener('click', updateLyricsDisplay); | |
| loadSampleBtn.addEventListener('click', loadSampleLyrics); | |
| textSizeSelect.addEventListener('change', updateTextSize); | |
| fontFamilySelect.addEventListener('change', updateFontFamily); | |
| // Set initial text size and font | |
| updateTextSize(); | |
| updateFontFamily(); | |
| }); | |
| // Functions | |
| function updateLyricsDisplay() { | |
| const lyrics = lyricsInput.value.trim(); | |
| if (!lyrics) return; | |
| // Split into paragraphs | |
| const paragraphs = lyrics.split('\n\n'); | |
| // Clear and rebuild display | |
| lyricsDisplay.innerHTML = ''; | |
| paragraphs.forEach(para => { | |
| const paraElement = document.createElement('p'); | |
| paraElement.className = 'mb-4'; | |
| // Split into words and wrap each word in a span | |
| const words = para.split(/(\s+)/).filter(word => word.trim().length > 0); | |
| words.forEach(word => { | |
| const wordSpan = document.createElement('span'); | |
| wordSpan.className = 'word'; | |
| wordSpan.textContent = word + ' '; | |
| paraElement.appendChild(wordSpan); | |
| }); | |
| lyricsDisplay.appendChild(paraElement); | |
| }); | |
| // Store words for highlighting | |
| words = Array.from(lyricsDisplay.querySelectorAll('.word')); | |
| currentWordIndex = 0; | |
| progressBar.style.width = '0%'; | |
| } | |
| function startKaraoke() { | |
| if (words.length === 0) { | |
| alert('Please enter lyrics first!'); | |
| return; | |
| } | |
| if (isPlaying) return; | |
| isPlaying = true; | |
| playBtn.classList.add('hidden'); | |
| pauseBtn.classList.remove('hidden'); | |
| // Reset highlighting | |
| words.forEach(word => { | |
| word.classList.remove('active'); | |
| word.style.color = ''; | |
| }); | |
| currentWordIndex = 0; | |
| progressBar.style.width = '0%'; | |
| // Start time counter | |
| startTime = Date.now() - elapsedTime; | |
| updateTimeCounter(); | |
| timeInterval = setInterval(updateTimeCounter, 10); | |
| // Start highlighting words | |
| const speed = parseInt(speedControl.value); | |
| highlightInterval = setInterval(highlightNextWord, speed); | |
| } | |
| function pauseKaraoke() { | |
| if (!isPlaying) return; | |
| isPlaying = false; | |
| pauseBtn.classList.add('hidden'); | |
| playBtn.classList.remove('hidden'); | |
| clearInterval(highlightInterval); | |
| clearInterval(timeInterval); | |
| } | |
| function stopKaraoke() { | |
| isPlaying = false; | |
| clearInterval(highlightInterval); | |
| clearInterval(timeInterval); | |
| playBtn.classList.remove('hidden'); | |
| pauseBtn.classList.add('hidden'); | |
| // Reset all words | |
| words.forEach(word => { | |
| word.classList.remove('active'); | |
| word.style.color = ''; | |
| }); | |
| currentWordIndex = 0; | |
| progressBar.style.width = '0%'; | |
| // Reset timer | |
| elapsedTime = 0; | |
| updateTimeCounter(); | |
| } | |
| function highlightNextWord() { | |
| // Reset previous word | |
| if (currentWordIndex > 0) { | |
| words[currentWordIndex - 1].classList.remove('active'); | |
| } | |
| // Check if we've reached the end | |
| if (currentWordIndex >= words.length) { | |
| stopKaraoke(); | |
| return; | |
| } | |
| // Highlight current word | |
| const currentWord = words[currentWordIndex]; | |
| currentWord.classList.add('active'); | |
| currentWord.style.color = highlightColor; | |
| // Scroll to keep word visible | |
| currentWord.scrollIntoView({ behavior: 'smooth', block: 'center' }); | |
| // Update progress | |
| const progress = (currentWordIndex / words.length) * 100; | |
| progressBar.style.width = `${progress}%`; | |
| currentWordIndex++; | |
| } | |
| function setHighlightColor(color) { | |
| highlightColor = color; | |
| // Update active word if playing | |
| if (isPlaying && currentWordIndex > 0) { | |
| words[currentWordIndex - 1].style.color = color; | |
| } | |
| } | |
| function updateTextSize() { | |
| const size = textSizeSelect.value; | |
| lyricsDisplay.className = `lyrics-container p-6 text-center ${size} leading-relaxed ${lyricsDisplay.className.split(' ').find(c => c.startsWith('font-')) || 'font-roboto'}`; | |
| } | |
| function updateFontFamily() { | |
| const font = fontFamilySelect.value; | |
| lyricsDisplay.className = `lyrics-container p-6 text-center ${textSizeSelect.value} leading-relaxed ${font}`; | |
| } | |
| function updateTimeCounter() { | |
| if (isPlaying) { | |
| elapsedTime = Date.now() - startTime; | |
| } | |
| const minutes = Math.floor(elapsedTime / 60000); | |
| const seconds = Math.floor((elapsedTime % 60000) / 1000); | |
| const milliseconds = Math.floor((elapsedTime % 1000) / 10); | |
| timeCounter.textContent = | |
| `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(2, '0')}`; | |
| } | |
| function loadSampleLyrics() { | |
| const sampleLyrics = `[Verse 1] | |
| I see trees of green, red roses too | |
| I see them bloom for me and you | |
| And I think to myself what a wonderful world | |
| [Verse 2] | |
| I see skies of blue and clouds of white | |
| The bright blessed days, the dark sacred nights | |
| And I think to myself what a wonderful world | |
| [Bridge] | |
| The colors of the rainbow so pretty in the sky | |
| Are also on the faces of people going by | |
| I see friends shaking hands saying how do you do | |
| They're really saying I love you | |
| [Verse 3] | |
| I hear babies cry, I watch them grow | |
| They'll learn much more than I'll ever know | |
| And I think to myself what a wonderful world | |
| Yes, I think to myself what a wonderful world`; | |
| lyricsInput.value = sampleLyrics; | |
| updateLyricsDisplay(); | |
| } | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=bibbler/karaoke-master" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> | |
| </html> |