Created
October 23, 2025 05:56
-
-
Save braebo/660ff6d8d3a2d3a464f033222f09c3b7 to your computer and use it in GitHub Desktop.
Forces vertical arrow keys to always control volume instead of scrolling.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Youtube Arrow Key Fix | |
| // @namespace Violentmonkey Scripts | |
| // @match https://www.youtube.com/watch?v=* | |
| // @grant none | |
| // @version 1.0 | |
| // @author braebo | |
| // @description Forces vertical arrow keys to always control volume instead of scrolling. | |
| // @note Hold alt for 1% or shift for 10% volume changes. | |
| // ==/UserScript== | |
| document.addEventListener('keydown', fixArrowKeys, { capture: true, passive: false }) | |
| function fixArrowKeys(event) { | |
| if (event.key === 'ArrowUp' || event.key === 'ArrowDown') { | |
| if ( | |
| document.activeElement?.tagName === 'INPUT' || | |
| document.activeElement?.isContentEditable // comment box | |
| ) { | |
| return | |
| } | |
| const player = document.getElementById('movie_player') | |
| // Relinquish control when the player is focused unless modifiers are pressed. | |
| if (player === document.activeElement && !event.altKey && !event.shiftKey) { | |
| return | |
| } | |
| event.preventDefault() | |
| event.stopPropagation() | |
| let step = event.altKey ? 1 : event.shiftKey ? 10 : 5 | |
| if (event.key === 'ArrowDown') step = -step | |
| const current = player?.getVolume?.() | |
| if (!isNaN(current)) { | |
| player.setVolume(Math.round(current + step)) | |
| } | |
| showVolumePopup(player) | |
| } | |
| } | |
| let popup = null | |
| let fade_timer = null | |
| let fade_animation = null | |
| function showVolumePopup(player) { | |
| clearTimeout(fade_timer) | |
| if (!popup) { | |
| popup = document.createElement('div') | |
| popup.id = 'volume-popup' | |
| popup.style.cssText = /* s */ ` | |
| display: inline-block; | |
| position: absolute; | |
| top: 10%; | |
| left: 0; | |
| right: 0; | |
| width: fit-content; | |
| margin: 0 auto; | |
| padding: 10px 20px; | |
| color: #eee; | |
| background: rgba(0, 0, 0, .5); | |
| pointer-events: none; | |
| border-radius: 3px; | |
| font-family: "YouTube Noto", Roboto, Arial, Helvetica, sans-serif; | |
| font-size: 175%; | |
| line-height: 1.3; | |
| -webkit-font-smoothing: antialiased; | |
| z-index: 1000; | |
| ` | |
| player.appendChild(popup) | |
| } | |
| fade_animation?.cancel() | |
| fade_animation = popup.animate( | |
| { opacity: 1 }, | |
| { duration: 100, easing: 'ease-out', fill: 'forwards' }, | |
| ) | |
| const current = player.getVolume?.() | |
| if (!isNaN(current)) { | |
| popup.textContent = `${current}%` | |
| } | |
| fade_timer = setTimeout(() => { | |
| fade_animation = popup?.animate({ opacity: 0 }, { duration: 100, easing: 'ease-out' }) | |
| fade_animation?.finished.then(() => { | |
| popup?.remove() | |
| popup = fade_animation = null | |
| }) | |
| }, 500) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment