`.play()` failed because the user didn't interact with the document first issue
Asked Answered
K

3

9

I want to play sound, after my page has loaded.

Here is my JavaScript code:

var playAudio = $("#sound")[0];
playAudio.play();

Here is my HTML code:

<audio id="sound">
  <source src="../music/hide.ogg" type="audio/ogg">
  <source src="../music/hide.mp3" type="audio/mp3">
</audio>

Console output:

Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first.

Kuntz answered 20/11, 2020 at 12:47 Comment(0)
K
6

Most browsers block any audio that is played by a web page and is not related to user interaction (see https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide). With user interaction I mean things like pressing a button. You can't do anything against that.

But you could add a start button that displays the page and starts the music, like this:

const startbtn = document.getElementById("startbtn");
const content = document.getElementById("content");
const loader = document.getElementById("loader");
const music = document.getElementById("music");

//Add an event listner to the start button
startbtn.addEventListener("click", () => {
  //Show the loader and hide the button
  loader.style.display = "";
  startbtn.style.display = "none";
  //Wait for the music to start
  music.play().then(() => {
    //Hide the loader and show the content
    content.style.display = "";
    loader.style.display = "none";
  });
});
<!-- The start button --->
<button id="startbtn">Start</button>

<!-- The actual content, hidden by default -->
<div id="content" style="display: none;">
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
  <p>Bla</p>
</div>

<!-- This is displayed when the user has pressed the start button, but the music is still loading -->
<div id="loader" style="display: none;">
  <p>Loading ...</p>
</div>

<!-- The music. It is started by the Javascript -->
<audio id="music" src="https://opengameart.org/sites/default/files/audio_preview/swing_0.mp3.ogg" style="display: none;" loop></audio>
Kalsomine answered 20/11, 2020 at 12:51 Comment(5)
i want to do thing like when i entering a web site music is playing but if user want to stop so he just press a button.Can i do this?and how?Kuntz
No, the browser will block the audio automatically. But you could display only a start button when the website is loaded and show the whole page and play the music when the start button was pressed. But I don't know if that fits your scenario.Kalsomine
how can i make button before web site loaded?Kuntz
what if the button clicked by jquery... is that make sense of playing it automatically?Junction
Is there any way "autoplay"?Anhanhalt
T
1

When this happens on Google Chrome, the error is currently showing a link to this page: https://developer.chrome.com/blog/autoplay/.

As the post explains, Google Chrome keeps track of an individual's propensity to consume media on a site, using a metric called MEI (Media Engagement Index).

Based on that metric, an attempt to play sound on page load may or may not work. You can see a list of the scores of which Chrome keeps track by going to chrome://media-engagement/ on your browser.

To handle cases in which the play on page load doesn't work, the post suggests the following code snippet:

var promise = document.querySelector('video').play();

if (promise !== undefined) {
  promise.then(_ => {
    // Autoplay started!
  }).catch(error => {
    // Autoplay was prevented.
    // Show a "Play" button so that user can start playback.
  });
}

In your case it could be:

const playAudio = $("#sound")[0];
const promise = playAudio.play();

let playedOnLoad;

if (promise !== undefined) {
  promise.then(_ => {
    // Autoplay started!
    playedOnLoad = true;
  }).catch(error => {
    // Autoplay was prevented.
    playedOnLoad = true;
  });
}

At this point you can use the playedOnLoad value to show a button or add a listener to some other types of user interaction.

Another option (the one I'm adopting) is to always show a toggle for the sound, and to change it's state (play/stop or enable/disable) based on the result of the promise.

Please remember that the MEI score is also saved for your local development environment, so, based on your personal interaction with the page, the sound may or may not start on page load even in your local environment.

Thames answered 25/10, 2023 at 7:37 Comment(0)
T
0

This is my code and works fine. I added two params to Notification class object

silent: false, requireInteraction: false

import silenceSound from '../assets/sounds/light.mp3';

export const playSound = async (soundPathOrBase64Data) => {
  var sound;
  if (soundPathOrBase64Data && soundPathOrBase64Data.toString().length) {
    sound = new Audio(soundPathOrBase64Data);
  } else {
    sound = new Audio(silenceSound);
  }
  sound.load();
  var promise = sound.play();
  if (promise) {
    //Older browsers may not return a promise, according to the MDN website
    promise.catch(function (error) {
      console.error(error);
    });
  }
};

function sendNotification(messageObj) {
  if (document.hidden) {
    const notification = new Notification('New message from Chat', {
      icon: 'https://cdn-icons-png.flaticon.com/512/733/733585.png',
      body: `@${messageObj?.userName || messageObj?.userEmail}: ${messageObj?.message || ''}`,
      silent: false,
      requireInteraction: false
    });

    playSound();

    notification.onclick = () =>
      function () {
        window.open('http://localhost:5500');
      };
  }
}

export default function checkPageStatus(message) {
  const localUser = localStorage.getItem('chat-app-user');
  const parsedUser = localUser ? JSON.parse(localUser) : undefined;

  if (message?.sender !== parsedUser?._id) {
    if (!('Notification' in window)) {
      alert('This browser does not support system notifications!');
    } else if (Notification.permission === 'granted' || Notification.permission === 'default') {
      sendNotification(message);
    } else if (Notification.permission !== 'denied') {
      Notification.requestPermission((permission) => {
        if (permission === 'granted') {
          sendNotification(message);
        }
      });
    }
  }
}
Tara answered 30/8, 2024 at 10:24 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.