Guided Relaxation Audio Player

Guided Relaxation Audio Player

Select a session, press play, and unwind.

Select a session below

0:00 0:00

Sessions

${script.duration}

`).join(''); }; const showInfo = (message) => { infoMessageDiv.textContent = message; infoMessageDiv.classList.remove('hidden'); }; const formatTime = (seconds) => { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs.toString().padStart(2, '0')}`; }; // --- 5. PLAYER LOGIC --- const selectTrack = (id) => { stopPlayback(); currentTrack = relaxationScripts.find(s => s.id === id); document.querySelectorAll('.playlist-item').forEach(item => { item.classList.toggle('active', item.dataset.id === id); }); currentTrackTitle.textContent = currentTrack.title; currentTrackDuration.textContent = currentTrack.duration; utterance = new SpeechSynthesisUtterance(currentTrack.script); utterance.onend = handlePlaybackEnd; utterance.onboundary = handleBoundary; // A trick to get duration: estimate based on word count const estimatedDuration = (currentTrack.script.split(' ').length / 2.5); // Avg 150 WPM totalTimeEl.textContent = formatTime(estimatedDuration); }; const handlePlayPause = () => { if (!currentTrack) { showInfo("Please select a session from the playlist first."); return; } if (synth.speaking) { if (synth.paused) { synth.resume(); playIcon.classList.add('hidden'); pauseIcon.classList.remove('hidden'); } else { synth.pause(); playIcon.classList.remove('hidden'); pauseIcon.classList.add('hidden'); } } else { synth.speak(utterance); playIcon.classList.add('hidden'); pauseIcon.classList.remove('hidden'); startProgressTracker(); } }; const stopPlayback = () => { if (synth.speaking) synth.cancel(); handlePlaybackEnd(); }; const handlePlaybackEnd = () => { playIcon.classList.remove('hidden'); pauseIcon.classList.add('hidden'); clearInterval(progressInterval); progressBar.style.width = '0%'; currentTimeEl.textContent = '0:00'; }; const startProgressTracker = () => { let elapsedTime = 0; const estimatedDuration = (currentTrack.script.split(' ').length / 2.5); progressInterval = setInterval(() => { if (synth.speaking && !synth.paused) { elapsedTime++; const progress = (elapsedTime / estimatedDuration) * 100; progressBar.style.width = `${Math.min(progress, 100)}%`; currentTimeEl.textContent = formatTime(elapsedTime); } }, 1000); }; const handleBoundary = (event) => { // This event can be used for more advanced features like highlighting text }; // --- 6. PDF GENERATION --- const downloadScript = () => { if (!currentTrack) { showInfo("Please select a session to download its script."); return; } const { jsPDF } = window.jspdf; const pdf = new jsPDF(); pdf.setFontSize(18); pdf.text(currentTrack.title, 20, 20); pdf.setFontSize(12); const textLines = pdf.splitTextToSize(currentTrack.script.replace(/\.\.\./g, '\n\n'), 170); pdf.text(textLines, 20, 30); pdf.save(`${currentTrack.title.replace(/ /g, '_')}_Script.pdf`); }; // --- 7. EVENT LISTENERS --- const setupEventListeners = () => { playPauseBtn.addEventListener('click', handlePlayPause); stopBtn.addEventListener('click', stopPlayback); downloadBtn.addEventListener('click', downloadScript); playlistContainer.addEventListener('click', (e) => { const targetItem = e.target.closest('.playlist-item'); if (targetItem) { selectTrack(targetItem.dataset.id); } }); }; // --- INITIALIZE THE PLAYER --- init(); });
Scroll to Top