Skip to content

Instantly share code, notes, and snippets.

@leyuskckiran1510
Last active November 18, 2025 05:08
Show Gist options
  • Select an option

  • Save leyuskckiran1510/8dc3e1c2f70ffbc447f1c1dc2a009898 to your computer and use it in GitHub Desktop.

Select an option

Save leyuskckiran1510/8dc3e1c2f70ffbc447f1c1dc2a009898 to your computer and use it in GitHub Desktop.
Video Controller

Video Controller - Tampermonkey Script

A userscript that adds keyboard shortcuts for controlling videos on any webpage.

Keyboard Shortcuts (hold Shift + key):

  • Shift+S/A - Speed up or slow down playback
  • Shift+W - Increase volume
  • Shift+D - Unmute
  • Shift+M - Toggle mute
  • Shift+Z/X - Skip forward or backward 10 seconds
  • Shift+R - Reset speed to normal
  • Shift+P - Play or pause
  • Shift+F - Fullscreen
  • Shift+Q - Copy current frame to clipboard

The script works on most video sites and will find videos inside iframes. Shortcuts are disabled when you're typing in text fields so they won't interfere with normal use. A small notification appears briefly to confirm each action.

Useful for watching videos when you want quick keyboard access to common controls without reaching for the mouse.

Note:- both code and the readme was generated using claude

// ==UserScript==
// @name Advanced Video Controller
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Control video playback with keyboard shortcuts (works with iframes)
// @match *://*/*
// @grant GM_setClipboard
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
const SPEED_STEP = 0.25;
const VOLUME_STEP = 0.1;
const SEEK_STEP = 10;
function getAllVideos() {
const videos = Array.from(document.querySelectorAll('video'));
const iframes = document.querySelectorAll('iframe');
iframes.forEach(iframe => {
try {
const iframeVideos = iframe.contentDocument?.querySelectorAll('video');
if (iframeVideos) {
videos.push(...Array.from(iframeVideos));
}
} catch (e) {}
});
return videos;
}
function getActiveVideo() {
const videos = getAllVideos();
const playing = videos.find(v => !v.paused);
return playing || videos[0];
}
function showNotification(message) {
const existing = document.getElementById('video-ctrl-notification');
if (existing) existing.remove();
const notification = document.createElement('div');
notification.id = 'video-ctrl-notification';
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 12px 20px;
border-radius: 6px;
font-family: monospace;
font-size: 14px;
z-index: 999999;
pointer-events: none;
`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 1500);
}
async function captureFrame(video) {
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0);
try {
const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
const item = new ClipboardItem({'image/png': blob});
await navigator.clipboard.write([item]);
showNotification('Frame copied to clipboard');
} catch (e) {
canvas.toBlob(blob => {
const url = URL.createObjectURL(blob);
GM_setClipboard(url);
showNotification('Frame URL copied (fallback)');
});
}
}
document.addEventListener('keydown', (e) => {
if (!e.shiftKey) return;
const activeElement = document.activeElement;
const isInputActive = activeElement && (
activeElement.tagName === 'INPUT' ||
activeElement.tagName === 'TEXTAREA' ||
activeElement.isContentEditable
);
if (isInputActive) return;
const video = getActiveVideo();
if (!video) return;
let handled = true;
switch(e.key.toLowerCase()) {
case 's':
video.playbackRate = Math.min(video.playbackRate + SPEED_STEP, 16);
showNotification(`Speed: ${video.playbackRate.toFixed(2)}x`);
break;
case 'a':
video.playbackRate = Math.max(video.playbackRate - SPEED_STEP, 0.25);
showNotification(`Speed: ${video.playbackRate.toFixed(2)}x`);
break;
case 'w':
video.volume = Math.min(video.volume + VOLUME_STEP, 1);
showNotification(`Volume: ${Math.round(video.volume * 100)}%`);
break;
case 'd':
video.muted = false;
showNotification('Unmuted');
break;
case 'z':
video.currentTime = Math.min(video.currentTime + SEEK_STEP, video.duration);
showNotification(`+${SEEK_STEP}s`);
break;
case 'x':
video.currentTime = Math.min(video.currentTime - SEEK_STEP, video.duration);
showNotification(`-${SEEK_STEP}s`);
break;
case 'q':
captureFrame(video);
break;
case 'r':
video.playbackRate = 1.0;
showNotification('Speed: 1.0x (reset)');
break;
case 'm':
video.muted = !video.muted;
showNotification(video.muted ? 'Muted' : 'Unmuted');
break;
case 'f':
if (video.requestFullscreen) {
video.requestFullscreen();
} else if (document.fullscreenElement) {
document.exitFullscreen();
}
break;
case 'p':
if (video.paused) {
video.play();
showNotification('Playing');
} else {
video.pause();
showNotification('Paused');
}
break;
case 'c':
showNotification(`${SEEK_STEP}s`);
break;
default:
handled = false;
}
if (handled) {
e.preventDefault();
e.stopPropagation();
}
}, true);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment