Skip to content

Instantly share code, notes, and snippets.

@mktwo
Last active July 21, 2023 09:39
Show Gist options
  • Save mktwo/100d7b5336cc9b8e2d778c528a7ae144 to your computer and use it in GitHub Desktop.
Save mktwo/100d7b5336cc9b8e2d778c528a7ae144 to your computer and use it in GitHub Desktop.
A userscript that compresses YouTube audio so loud sections (such as intros that are at an average of 0dB when the video is at -6dB) aren't as awful.
// ==UserScript==
// @name YouTube Audio Compressor/Limiter
// @description A somewhat passable solution to YouTubers who don't know how to look at a dB meter while editing their videos... (Makes very loud sections not as deafening!)
// @author Tim (@ineedfocus)
// @include https://www.youtube.com/*
// @include http://www.youtube.com/*
// ==/UserScript==
//Apply compression to YouTube video
function fixAudio(element) {
try{
//Set up the audio context
var myAudio = element;
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
if(audioCtx != null && myAudio != null){
var source = audioCtx.createMediaElementSource(myAudio);
//Set up the audio processing
//Compressor:
var compressor = audioCtx.createDynamicsCompressor();
compressor.threshold.setValueAtTime(-24, audioCtx.currentTime); //This could probably go up a bit, but that'll make it less effective at canceling out loud sections
compressor.knee.setValueAtTime(12, audioCtx.currentTime);
compressor.ratio.setValueAtTime(4, audioCtx.currentTime); //Lower this to 2 or 3 if the pumping effect is a bit too strong for your tastes and you're okay with more loudness (1 = no compression)
compressor.attack.setValueAtTime(0, audioCtx.currentTime); //Probably don't change this, it'll give you blasts of the original loud stuff before kicking in.
compressor.release.setValueAtTime(0.15, audioCtx.currentTime);
//Limit volume:
var gainNode = audioCtx.createGain();
gainNode.gain.setValueAtTime(0.8, audioCtx.currentTime); //0 = mute, 1 is default
//Source audio -> compressor -> gain
source.connect(compressor);
compressor.connect(gainNode);
//Send processed audio to where it needs to be
gainNode.connect(audioCtx.destination);
}
}catch(e){
console.error(e);
}
}
//Wait for YouTube to insert their video element
function waitForElement(selector) {
return new Promise(function(resolve, reject) {
var element = document.querySelector(selector);
if(element) {
resolve(element);
return;
}
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
var nodes = Array.from(mutation.addedNodes);
for(var node of nodes) {
if(node.matches && node.matches(selector)) {
observer.disconnect();
resolve(node);
return;
}
};
});
});
observer.observe(document.documentElement, { childList: true, subtree: true });
});
}
//Do the thing!
waitForElement("video").then(function(element) {
//console.log("Video element found, fixing audio!");
fixAudio(element);
});
@SirFireNewt
Copy link

Just became aware of this and really appreciate this tool!
Only issue I have is that it seems Youtube's playback speed doesn't work with the script enabled for me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment