Skip to main content

How to Resolve the "Uncaught DOMException: play() failed because the user didn't interact with the document first" Error in JavaScript

The Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first is a common error in modern web development. It occurs when your JavaScript code tries to play a video or audio file with sound before the user has "activated" the page with a click, tap, or key press.

This is not a bug, but a deliberate browser policy designed to protect users from unexpected and unwanted noise. This guide will explain this policy and show you the correct ways to implement autoplay functionality that respects these browser rules.

The Core Problem: Browser Autoplay Policies

To prevent a poor user experience, nearly all modern web browsers (Chrome, Firefox, Safari) have strict autoplay policies. These policies can be summarized as:

  • Muted autoplay is always allowed. You can start a video or audio element programmatically as long as it begins in a muted state.
  • Autoplay with sound is only allowed if the user has interacted with the site. A user interaction is a clear signal (like a click or keydown event) that the user is engaged with the page and is likely to be okay with audio starting.

If you try to call .play() on an unmuted media element before the user has interacted with the page, the browser will block it and throw the DOMException error.

This is the most common and reliable way to have a video start playing automatically on page load, such as for a background video.

For example, you want a video to start playing as soon as the page loads, but your code is throwing an error.

// Problem: This will fail on page load because the video is not muted.
const video = document.getElementById('my-video');
video.play(); // ⛔️ DOMException: play() failed...

The solution is to add the muted and autoplay attributes directly to your HTML <video> element.

<video
src="path/to/my-video.mp4"
width="500"
autoplay
muted
loop
playsinline
>
</video>

where:

  • autoplay: Tells the browser to try to play the video as soon as it can.
  • muted: This is the key. It ensures the video will play without sound, which complies with the browser's autoplay policy.
  • loop: Makes the video restart when it finishes.
  • playsinline: Important for iOS to prevent the video from automatically going full-screen.

You can also set this programmatically in JavaScript:

const video = document.getElementById('my-video');
video.muted = true; // Set the muted property
video.play(); // Now this will work

Solution 2: Autoplay with Sound After User Interaction

If your goal is to play a video with sound, you must wait for the user to interact with the page first. The most common way to do this is to trigger the playback from a click event listener.

For example, you can't play an unmuted video on page load, so how do you start it with sound?

The solution is to tie the .play() call to a user-initiated event, like a button click.

HTML:

<video id="main-video" src="path/to/video.mp4" width="500"></video>
<button id="play-btn">Play Video</button>

JavaScript:

const video = document.getElementById('main-video');
const playButton = document.getElementById('play-btn');

playButton.addEventListener('click', () => {
// This is allowed because it was triggered by a user click.
video.play();
});
note

Because the video.play() call is a direct result of a user interaction, the browser will permit the video to play with sound. This same principle applies to <audio> elements.

How to Programmatically Play Media

The .play() method on a media element returns a Promise. It's a good practice to handle this promise to catch any potential errors gracefully, even if you are following the rules.

async function playMedia(element) {
try {
await element.play();
console.log('Media is playing successfully.');
} catch (error) {
// The play() request was interrupted or failed.
console.error('Failed to play media:', error);
}
}

// Example usage:
const video = document.getElementById('main-video');
const playButton = document.getElementById('play-btn');

playButton.addEventListener('click', () => {
playMedia(video);
});

Conclusion

The "play() failed because the user didn't interact" error is a protective feature of modern browsers, not a bug in your code. To work with it, you must follow the rules of the autoplay policy.

  • If you want media to play automatically on page load, it must be muted. The easiest way is to add the autoplay and muted attributes to your <video> or <audio> tag.
  • If you want media to play with sound, you must initiate the playback from a user interaction event, such as a 'click' or 'keydown'.
  • The .play() method returns a Promise. Handling it with async/await or .catch() is a robust way to prevent uncaught promise errors.