// ==UserScript== // @name Instagram Background Play // @description Keep Instagram videos and reels playing when you switch tabs or apps. Stops Instagram from pausing playback the moment you look away, and keeps picture-in-picture live instead of frozen. // @version 1.0.3 // @author Zingzy // @namespace https://github.com/Zingzy/instagram-background-play // @homepageURL https://github.com/Zingzy/instagram-background-play // @supportURL https://github.com/Zingzy/instagram-background-play/issues // @match https://www.instagram.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=instagram.com // @license MIT // @run-at document-start // @grant none // @downloadURL https://raw.githubusercontent.com/Zingzy/instagram-background-play/main/instagram-background-play.user.js // @updateURL https://raw.githubusercontent.com/Zingzy/instagram-background-play/main/instagram-background-play.user.js // ==/UserScript== (function () { 'use strict'; // Instagram pauses playback as soon as the tab or window loses focus. // It detects this through the Page Visibility API and focus events, // and additionally calls pause() from its own player logic. // This script defuses all three paths. // Keep a handle on the real visibility getter before spoofing it, // so we can still tell when the tab is genuinely hidden. const nativeVisibility = Object.getOwnPropertyDescriptor( Document.prototype, 'visibilityState' ).get; const reallyHidden = () => nativeVisibility.call(document) !== 'visible'; // 1. Spoof the Page Visibility API: the page always reports "visible". Object.defineProperty(Document.prototype, 'visibilityState', { get: () => 'visible', configurable: true, }); Object.defineProperty(Document.prototype, 'hidden', { get: () => false, configurable: true, }); document.hasFocus = () => true; // 2. Swallow the events Instagram listens to for detecting you leaving. // Capturing on window runs before any page listener can react. const block = (e) => e.stopImmediatePropagation(); for (const type of [ 'visibilitychange', 'webkitvisibilitychange', 'blur', 'focusout', 'pagehide', 'freeze', ]) { window.addEventListener(type, block, true); } // 3. The hammer: drop programmatic pause() calls while the tab is // actually backgrounded. Manual pauses still work, since they happen // while the tab is visible or go through browser UI (like // picture-in-picture controls) which bypasses this method entirely. const nativePause = HTMLMediaElement.prototype.pause; HTMLMediaElement.prototype.pause = function () { if (reallyHidden()) return; return nativePause.call(this); }; })();