-
-
Save tomhicks/6cb5e827723c4eaef638bf9f7686d2d8 to your computer and use it in GitHub Desktop.
/* | |
Copy this into the console of any web page that is interactive and doesn't | |
do hard reloads. You will hear your DOM changes as different pitches of | |
audio. | |
I have found this interesting for debugging, but also fun to hear web pages | |
render like UIs do in movies. | |
*/ | |
const audioCtx = new (window.AudioContext || window.webkitAudioContext)() | |
const observer = new MutationObserver(function(mutationsList) { | |
const oscillator = audioCtx.createOscillator() | |
oscillator.connect(audioCtx.destination) | |
oscillator.type = "sine" | |
oscillator.frequency.setValueAtTime( | |
Math.log(mutationsList.length + 5) * 880, | |
audioCtx.currentTime, | |
) | |
oscillator.start() | |
oscillator.stop(audioCtx.currentTime + 0.01) | |
}) | |
observer.observe(document, { | |
attributes: true, | |
childList: true, | |
subtree: true, | |
characterData: true, | |
}) |
I honestly didn't like it at first, then I realized how useful it can actually be :) . So I turned it into a basic Chrome extension: https://github.com/R4meau/plink-plonk
I'll be working on it every Sunday (very slow for now) and I'm open for any contributions at any time. Thanks for the head start @tomhicks.
I have moved out the constants into variables, to be able to play with the settings more easily.
I also added a random component. to make it sound less monotonous:
// origin: https://gist.github.com/tomhicks/6cb5e827723c4eaef638bf9f7686d2d8 , tomhicks/plink-plonk.js
const audioCtx = new (window.AudioContext || window.webkitAudioContext)()
var delay = 0.03
var freqbase = 600
var freqrnd = 400
const observer = new MutationObserver(function(mutationsList) {
const oscillator = audioCtx.createOscillator()
oscillator.connect(audioCtx.destination)
oscillator.type = "sine"
oscillator.frequency.setValueAtTime(
Math.log(mutationsList.length + 5) * (Math.random()*freqrnd + freqbase),
audioCtx.currentTime,
)
oscillator.start()
oscillator.stop(audioCtx.currentTime + delay)
})
observer.observe(document, {
attributes: true,
childList: true,
subtree: true,
characterData: true,
})
Then you can play with delay and freq on the fly.
Love this!
This one randomly decreases/increases tone until it reaches a threshold. Makes it a bit more harmonious:
// origin: https://gist.github.com/tomhicks/6cb5e827723c4eaef638bf9f7686d2d8 , tomhicks/plink-plonk.js
const audioCtx = new (window.AudioContext || window.webkitAudioContext)()
var delay = 0.03
// parameters of the random increment/decrement
var freqstart = 600
var freqlow = 400
var freqhigh = 900
var freqspeed = 50
// initial values
var freqinc = 25
var freq = 600
const observer = new MutationObserver(function(mutationsList) {
const oscillator = audioCtx.createOscillator()
oscillator.connect(audioCtx.destination)
oscillator.type = "sine"
oscillator.frequency.setValueAtTime(
Math.log(mutationsList.length + 5) * freq,
audioCtx.currentTime,
)
freq += freqinc
if (freq > freqhigh || freq<freqlow) {
// reset if we have reached the high or low bound
freq = freqstart
freqinc = (Math.random()-0.5)*freqspeed
}
oscillator.start()
oscillator.stop(audioCtx.currentTime + delay)
})
observer.observe(document, {
attributes: true,
childList: true,
subtree: true,
characterData: true,
})
Nice work! Check this out https://soundcode.now.sh too 🙂
Absolutely wonderful! 😄
awesome! My computer now sounds like one from a '70s TV serial
Could replace the pulses with Rice Krispies sounds. Now, web == tasty breakfast!
try this on a typing test website. very enjoyable lol
Added quantization and randomness + delay for a cuter effect 😇
https://gist.github.com/MarkArts/3d4217f957df8a30802a8cbf962fa204
// origin: https://gist.github.com/tomhicks/6cb5e827723c4eaef638bf9f7686d2d8 , tomhicks/plink-plonk.js
/*
Copy this into the console of any web page that is interactive and doesn't
do hard reloads. You will hear your DOM changes as different pitches of
audio.
I have found this interesting for debugging, but also fun to hear web pages
render like UIs do in movies.
*/
// dorian (-) C E F G- A B-
let scale = [
264, 330, 352, 391.1, 440, 488.9
]
scale = scale.concat(scale.map(x=>x*2))
console.log(scale)
function quantize(scale, freq) {
return scale.reduce(function(prev, curr){
return (Math.abs(curr - freq) < Math.abs(prev - freq) ? curr : prev);
});
}
const audioCtx = new (window.AudioContext || window.webkitAudioContext)()
const observer = new MutationObserver(observe)
function observe(mutationsList) {
// with delay
delayNote(gain => playNote(mutationsList, gain), 300, 0.2)
// without
// playNote(mutationsList)
}
// Compressor as final stage to prevent clipping
const compressor = audioCtx.createDynamicsCompressor()
compressor.threshold.setValueAtTime(-40, audioCtx.currentTime);
compressor.knee.setValueAtTime(40, audioCtx.currentTime);
compressor.ratio.setValueAtTime(12, audioCtx.currentTime);
compressor.attack.setValueAtTime(0, audioCtx.currentTime);
compressor.release.setValueAtTime(0.25, audioCtx.currentTime);
compressor.connect(audioCtx.destination)
async function playNote(mutationsList, gain = 1) {
audioCtx.resume()
const oscillator = audioCtx.createOscillator()
oscillator.type = "triangle"
const biquadFilter = audioCtx.createBiquadFilter();
biquadFilter.type = "lowpass";
const gainNode = audioCtx.createGain();
const panNode = audioCtx.createStereoPanner();
// Setup audio chain
oscillator.connect(biquadFilter);
biquadFilter.connect(gainNode);
gainNode.connect(panNode);
panNode.connect(compressor)
let freq = quantize(scale, 440 * (Math.random() * 3))
oscillator.frequency.setValueAtTime(
quantize(scale, freq),
audioCtx.currentTime,
)
// Low pass gate
biquadFilter.frequency.setValueAtTime(
quantize(scale, freq * 4),
audioCtx.currentTime
);
biquadFilter.frequency.setTargetAtTime(
freq,
audioCtx.currentTime,
0.09,
);
// accend the low pass gate with normal attenuatiob
gainNode.gain.setValueAtTime(
gain,
audioCtx.currentTime
);
gainNode.gain.setTargetAtTime(
0,
audioCtx.currentTime,
0.1,
);
// random stereo pan
panNode.pan.setValueAtTime(
Math.random() * 2 - 1,
audioCtx.currentTime
);
oscillator.start()
oscillator.stop(audioCtx.currentTime + 1)
}
async function delayNote(f, time, decay, gain = 1){
if (gain <= 0) {
return // stop repeats when they become inaudible
}
f(gain)
setTimeout( _ => delayNote(f, time, decay, gain - decay), time);
}
observer.observe(document, {
attributes: true,
childList: true,
subtree: true,
characterData: true,
})
Deploy this on production but use an Arnold Schwarzenegger sound bank which plays a random line at each DOM mutation.
Wow this is probably the best gist I've ever seen
love this
Added quantization and randomness + delay for a cuter effect 😇
https://gist.github.com/MarkArts/3d4217f957df8a30802a8cbf962fa204
// origin: https://gist.github.com/tomhicks/6cb5e827723c4eaef638bf9f7686d2d8 , tomhicks/plink-plonk.js /* Copy this into the console of any web page that is interactive and doesn't do hard reloads. You will hear your DOM changes as different pitches of audio. I have found this interesting for debugging, but also fun to hear web pages render like UIs do in movies. */ // dorian (-) C E F G- A B- let scale = [ 264, 330, 352, 391.1, 440, 488.9 ] scale = scale.concat(scale.map(x=>x*2)) console.log(scale) function quantize(scale, freq) { return scale.reduce(function(prev, curr){ return (Math.abs(curr - freq) < Math.abs(prev - freq) ? curr : prev); }); } const audioCtx = new (window.AudioContext || window.webkitAudioContext)() const observer = new MutationObserver(observe) function observe(mutationsList) { // with delay delayNote(gain => playNote(mutationsList, gain), 300, 0.2) // without // playNote(mutationsList) } // Compressor as final stage to prevent clipping const compressor = audioCtx.createDynamicsCompressor() compressor.threshold.setValueAtTime(-40, audioCtx.currentTime); compressor.knee.setValueAtTime(40, audioCtx.currentTime); compressor.ratio.setValueAtTime(12, audioCtx.currentTime); compressor.attack.setValueAtTime(0, audioCtx.currentTime); compressor.release.setValueAtTime(0.25, audioCtx.currentTime); compressor.connect(audioCtx.destination) async function playNote(mutationsList, gain = 1) { audioCtx.resume() const oscillator = audioCtx.createOscillator() oscillator.type = "triangle" const biquadFilter = audioCtx.createBiquadFilter(); biquadFilter.type = "lowpass"; const gainNode = audioCtx.createGain(); const panNode = audioCtx.createStereoPanner(); // Setup audio chain oscillator.connect(biquadFilter); biquadFilter.connect(gainNode); gainNode.connect(panNode); panNode.connect(compressor) let freq = quantize(scale, 440 * (Math.random() * 3)) oscillator.frequency.setValueAtTime( quantize(scale, freq), audioCtx.currentTime, ) // Low pass gate biquadFilter.frequency.setValueAtTime( quantize(scale, freq * 4), audioCtx.currentTime ); biquadFilter.frequency.setTargetAtTime( freq, audioCtx.currentTime, 0.09, ); // accend the low pass gate with normal attenuatiob gainNode.gain.setValueAtTime( gain, audioCtx.currentTime ); gainNode.gain.setTargetAtTime( 0, audioCtx.currentTime, 0.1, ); // random stereo pan panNode.pan.setValueAtTime( Math.random() * 2 - 1, audioCtx.currentTime ); oscillator.start() oscillator.stop(audioCtx.currentTime + 1) } async function delayNote(f, time, decay, gain = 1){ if (gain <= 0) { return // stop repeats when they become inaudible } f(gain) setTimeout( _ => delayNote(f, time, decay, gain - decay), time); } observer.observe(document, { attributes: true, childList: true, subtree: true, characterData: true, })
This one's great~ 😇
Awesome idea!
It would be nice to create some debugging library with sounds for Ajax Requests, Ajax failures, console errors etc.
@everaldo I'm working on it at the moment: https://github.com/r4meau/plink-plonk
Also, great idea about the console errors. Added this to the list of features in the README :). For the requests, it's already in the list.
@R4meau, that's awesome!
Maybe someone makes some chrome extension?
And the bookmarklet version:
javascript:(function(){const%20audioCtx=new(window.AudioContext||window.webkitAudioContext);const%20oscillator=audioCtx.createOscillator();oscillator.connect(audioCtx.destination);oscillator.type="sine";let%20numItems=0;oscillator.frequency.setValueAtTime(1,audioCtx.currentTime);oscillator.start();const%20observer=new%20MutationObserver(function(mutationsList){numItems+=mutationsList.length;oscillator.frequency.setValueAtTime(Math.log(numItems+1)*440,audioCtx.currentTime);setTimeout(()=>{numItems-=mutationsList.length;if(numItems===0){oscillator.frequency.setValueAtTime(1,audioCtx.currentTime)}else{oscillator.frequency.setValueAtTime(Math.log(numItems+1)*440,audioCtx.currentTime)}},100)});observer.observe(document,{attributes:true,childList:true,subtree:true,characterData:true})})();
Maybe someone makes some chrome extension?
Yeah, let's do it... (adding to backlog) 😅
@aibolik Already in the making: https://github.com/r4meau/plink-plonk
Feel free to fork and contribute... or start your own. 😄
I'm gonna go slow on it for now (Sunday only), but I'll accept useful PRs at anytime.
Could anyone add reload support? This is so wonderful 🥰 Maybe via localStorage? Or as Firefox Extension?
Its so helpful to find out the DOM manipulations happening behind the eyes!!. Good job @R4meau
Hahaha great idea! 👍 (could be a nice prank too)
Didn't work on Firefox, but Chrome is ✔️