fix iOS safari bug where next song wouldn't reliably play if PWA was in background
Hunter
Tuesday, November 4th, 2025 at 11:35 PM
529 }
530
531 animate();
532 +
533 + // iOS PWA background playback fix: use setInterval as backup
534 + // setInterval is less throttled than requestAnimationFrame in background
535 + backgroundPlaybackCheckInterval = setInterval(() => {
536 + if (!audio.paused && audio.duration && audio.currentTime >= audio.duration - 0.5) {
537 + console.log('Background check: song ended, triggering next');
538 + clearInterval(backgroundPlaybackCheckInterval);
539 + backgroundPlaybackCheckInterval = null;
540 +
541 + if (songs[currentSongIndex].looping) {
542 + audio.currentTime = 0;
543 + audio.play();
544 + } else {
545 + nextSong();
546 + }
547 + }
548 +
549 + // Update Media Session position state for iOS
550 + if ('mediaSession' in navigator && 'setPositionState' in navigator.mediaSession) {
551 + try {
552 + if (audio.duration && !isNaN(audio.duration) && isFinite(audio.duration)) {
553 + navigator.mediaSession.setPositionState({
554 + duration: audio.duration,
555 + playbackRate: audio.playbackRate,
556 + position: audio.currentTime
557 + });
558 + }
559 + } catch (e) {
560 + // Ignore errors from setPositionState
561 + }
562 + }
563 + }, 500); // Check every 500ms
564 }
565
566 function stopProgressBar() {
568 cancelAnimationFrame(animationFrameId);
569 animationFrameId = null;
570 }
571 + if (backgroundPlaybackCheckInterval !== null) {
572 + clearInterval(backgroundPlaybackCheckInterval);
573 + backgroundPlaybackCheckInterval = null;
574 + }
575 }
576
577 function resetProgressBar() {
591 let isDragging = false;
592 let wasPlayingBeforeDrag = false;
593 let pendingSeekPercentage = null;
594 +let backgroundPlaybackCheckInterval = null;
595
596 function updateVisualProgress(event) {
597 if (!playerReady) return;