NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Auto Unmute AutoPlay Videos
// @description Automatically unmutes videos that autoplay on page load, ignoring hidden ones (waits for user interaction to prevent autoplay blocking).
// @author Rami S Ejailat
// @copyright RamiEjailat (https://openuserjs.org/users/RamiEjailat)
// @updateURL https://openuserjs.org/meta/RamiEjailat/Auto_Unmute_AutoPlay_Videos.meta.js
// @downloadURL https://openuserjs.org/install/RamiEjailat/Auto_Unmute_AutoPlay_Videos.user.js
// @license MIT
// @version 20260522
// @namespace http://tampermonkey.net/
// @match *://*/*
// @grant none
// @icon https://cdn-icons-png.flaticon.com/64/4406/4406124.png
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// Flag to track if the browser's autoplay policy has been satisfied
let userHasInteracted = false;
// Helper function to determine if a video is actually visible to the user
const isVisible = (el) => {
const rect = el.getBoundingClientRect();
const style = window.getComputedStyle(el);
return rect.width > 10 &&
rect.height > 10 &&
style.display !== 'none' &&
style.visibility !== 'hidden' &&
style.opacity !== '0';
};
// Function to unmute and reset volume ONLY if visible AND user has interacted
const unmuteVideo = (video) => {
if (!userHasInteracted) return; // Prevent breaking the video before interaction
if (isVisible(video)) {
if (video.muted || video.volume === 0) {
video.muted = false;
if (video.volume === 0) video.volume = 1.0;
}
}
};
// Unlocks audio and unmutes existing videos upon first interaction
const handleInteraction = () => {
if (userHasInteracted) return;
userHasInteracted = true;
// Run through all videos and unmute them now that we are allowed to
document.querySelectorAll('video').forEach(video => {
if (!video.paused || video.autoplay) {
unmuteVideo(video);
}
});
// Clean up listeners once unlocked
['click', 'pointerdown', 'keydown'].forEach(evt => {
document.removeEventListener(evt, handleInteraction, true);
});
};
// Listen for the first user interaction to satisfy Autoplay Policy
['click', 'pointerdown', 'keydown'].forEach(evt => {
document.addEventListener(evt, handleInteraction, { capture: true, once: true });
});
// Intercept videos as soon as they trigger a 'play' event
document.addEventListener('play', (event) => {
if (event.target && event.target.tagName === 'VIDEO') {
unmuteVideo(event.target);
}
}, true);
// Check videos already present when the script injects
const checkExistingVideos = () => {
document.querySelectorAll('video').forEach(video => {
if (!video.paused || video.autoplay) {
unmuteVideo(video);
}
});
};
window.addEventListener('DOMContentLoaded', checkExistingVideos);
window.addEventListener('load', checkExistingVideos);
checkExistingVideos();
// Watch for dynamically added videos (Single Page Applications like React/Vue)
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
// If the added node is a video
if (node.tagName === 'VIDEO') {
if (node.autoplay || !node.paused) unmuteVideo(node);
}
// If the added node contains videos inside it
else if (node.querySelectorAll) {
node.querySelectorAll('video').forEach(video => {
if (video.autoplay || !video.paused) unmuteVideo(video);
});
}
});
});
});
// Start observing the document body for injected elements
if (document.body) {
observer.observe(document.body, { childList: true, subtree: true });
} else {
// Fallback if body isn't parsed yet
window.addEventListener('DOMContentLoaded', () => {
observer.observe(document.body, { childList: true, subtree: true });
});
}
})();