Last active
December 25, 2025 21:56
-
-
Save OutRite/a78c7ae77e9d3a2d80cf21f790941952 to your computer and use it in GitHub Desktop.
Userscript to let you toggle whether YouTube preserves pitch when changing video speed
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 Toggle preserve pitch | |
| // @namespace 534 | |
| // @match https://www.youtube.com/* | |
| // @grant none | |
| // @version 1.2 | |
| // @author 534 | |
| // @description Allows you to toggle preservesPitch on YouTube videos from the UI. | |
| // ==/UserScript== | |
| function toggle_preserve_pitch() { | |
| // Toggle preservesPitch on video | |
| const current_video = document.querySelector("video"); | |
| current_video.preservesPitch = !current_video.preservesPitch; | |
| // Toggle preserve pitch checkbox | |
| // We set based on the preservesPitch value and not the prior aria-checked value, to mitigate potential desyncs | |
| document.getElementById("preserve-pitch-container").setAttribute("aria-checked", current_video.preservesPitch); | |
| } | |
| function create_preserve_pitch_element() { | |
| // Outer container | |
| const mi_container = document.createElement("div"); | |
| mi_container.className = "ytp-menuitem"; | |
| mi_container.id = "preserve-pitch-container"; | |
| mi_container.setAttribute("role", "menuitemcheckbox"); | |
| mi_container.setAttribute("aria-checked", "true"); | |
| mi_container.setAttribute("tabindex", "0"); | |
| // Icon | |
| const icon_container = document.createElement("div"); | |
| icon_container.className = "ytp-menuitem-icon"; | |
| const svg_ns = "http://www.w3.org/2000/svg"; // https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS | |
| const icon_svg = document.createElementNS(svg_ns, "svg"); | |
| icon_svg.setAttribute("fill", "currentColor"); | |
| icon_svg.setAttribute("height", "24"); | |
| icon_svg.setAttribute("viewBox", "0 0 24 24"); | |
| icon_svg.setAttribute("width", "24"); | |
| const path_svg = document.createElementNS(svg_ns, "path"); | |
| path_svg.setAttribute("d", "M12 1c1.44 0 2.87.28 4.21.83a11 11 0 0 1 3.45 2.27l-1.81 1.05A9 9 0 0 0 3 12a9 9 0 0 0 18-.00l-.01-.44a8.99 8.99 0 0 0-.14-1.20l1.81-1.05A11.00 11.00 0 0 1 10.51 22.9 11 11 0 0 1 12 1Zm7.08 6.25-7.96 3.25a1.74 1.74 0 1 0 1.73 2.99l6.8-5.26a.57.57 0 0 0-.56-.98Z"); // this may or may not just be the youtube speed icon lol | |
| icon_svg.appendChild(path_svg); | |
| icon_container.appendChild(icon_svg); | |
| mi_container.appendChild(icon_container); | |
| // Label | |
| const label = document.createElement("div"); | |
| label.className = "ytp-menuitem-label"; | |
| label.textContent = "Preserve pitch"; | |
| mi_container.appendChild(label); | |
| // Checkbox | |
| const checkbox_container = document.createElement("div"); | |
| checkbox_container.className = "ytp-menuitem-content"; | |
| const checkbox = document.createElement("div"); | |
| checkbox.className = "ytp-menuitem-toggle-checkbox"; | |
| checkbox_container.appendChild(checkbox); | |
| mi_container.appendChild(checkbox_container); | |
| // Handle events | |
| // Click | |
| mi_container.addEventListener("click", toggle_preserve_pitch); | |
| mi_container.addEventListener("keydown", (e) => { // handling this means we get keyboard accessibility. youtube already does 90% of the work for us there | |
| if (e.key === " " || e.key === "Enter") { | |
| e.preventDefault(); | |
| toggle_preserve_pitch(); | |
| } | |
| }); | |
| // Done | |
| return mi_container; | |
| } | |
| function create_and_insert_element() { | |
| if (document.getElementsByTagName("video") && !document.getElementById("preserve-pitch-container")) { | |
| document.querySelector(".ytp-contextmenu > .ytp-popup-content > .ytp-panel > .ytp-panel-menu > .ytp-menuitem > .ytp-menuitem-content > .ytp-menuitem-toggle-checkbox").parentElement.parentElement.insertAdjacentElement("afterend", create_preserve_pitch_element()) | |
| } | |
| } | |
| setInterval(create_and_insert_element, 250); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment