Skip to content

Instantly share code, notes, and snippets.

@OutRite
Last active December 25, 2025 21:56
Show Gist options
  • Select an option

  • Save OutRite/a78c7ae77e9d3a2d80cf21f790941952 to your computer and use it in GitHub Desktop.

Select an option

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
// ==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