REDgifs CPU% Saver

Pause offscreen videos and gently fade in visible ones for smooth, efficient browsing.

// ==UserScript==
// @name         REDgifs CPU% Saver
// @version      1.4.1
// @description  Pause offscreen videos and gently fade in visible ones for smooth, efficient browsing.
// @author       Raizuto & ChatGPT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=redgifs.com
// @match        *://*.redgifs.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// @homepageURL  https://github.com/Raizuto/Tamper_Violentmonkey-Scripts/
// @supportURL   https://github.com/Raizuto/Tamper_Violentmonkey-Scripts/issues
// @namespace http://tampermonkey.net/
// ==/UserScript==

// Adjust the @match if you want to use this elsewhere. :D 
// Change the MAX_ACTIVE below to the desired number of allowed videos 
// to be loaded at once (this includes off screen videos)!
// Set to 1 by default for 21.5" monitors whether they are landscape or portrait.
// So if it doesn't work quite right for you tweak the Visible Threshold below

(() => {
  'use strict';
  const KEYS = { ENABLE: 'svs_enabled', DEBUG: 'svs_debug', PERF: 'svs_perf' };
  const MAX_ACTIVE = 1, OFFSET = 800, VISIBLE_THRESHOLD = 0.85, DELAY = 550;
  const get = (k, d) => GM_getValue(k, d);
  const set = (k, v) => GM_setValue(k, v);
  const log = (...a) => get(KEYS.DEBUG, false) && console.log('[SVS]', ...a);

  if (get(KEYS.ENABLE) === undefined) set(KEYS.ENABLE, true);
  if (get(KEYS.DEBUG) === undefined) set(KEYS.DEBUG, false);
  if (get(KEYS.PERF) === undefined) set(KEYS.PERF, true);

  if (!get(KEYS.ENABLE, true) || !get(KEYS.PERF, false)) return;

  const active = new Set();
  const videos = new Set();

  const io = new IntersectionObserver(entries => {
    for (const e of entries) {
      const v = e.target;
      if (!(v instanceof HTMLVideoElement)) continue;
      const mostlyVisible = e.intersectionRatio >= VISIBLE_THRESHOLD;
      if (mostlyVisible && active.size < MAX_ACTIVE) {
        if (v.dataset.fade !== '1') {
          v.style.opacity = 0;
          v.style.transition = 'opacity 0.5s ease';
          v.dataset.fade = '1';
        }
        setTimeout(() => {
          if (e.intersectionRatio >= VISIBLE_THRESHOLD) {
            v.play().catch(() => {});
            v.style.opacity = 1;
            active.add(v);
            log('Playing', v);
          }
        }, DELAY);
      } else if (!mostlyVisible) {
        v.pause();
        v.style.opacity = 0.6;
        v.currentTime = 0;
        active.delete(v);
        log('Paused', v);
      }
    }
  }, { rootMargin: `${OFFSET}px`, threshold: Array.from({ length: 11 }, (_, i) => i / 10) });

  const watch = () => {
    document.querySelectorAll('video').forEach(v => {
      if (!v.dataset.svs) {
        v.dataset.svs = '1';
        videos.add(v);
        io.observe(v);
        log('Observed', v);
      }
    });
  };

  const mo = new MutationObserver(() => watch());
  document.addEventListener('DOMContentLoaded', watch);
  mo.observe(document.documentElement, { childList: true, subtree: true });
})();