/* | |
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, | |
}) |
This is absolutely brilliant. And useful.
Here's a funny variation that speaks out the counts in different pitches/rates
const speechSynthesis = window.speechSynthesis
const min = 0
const max = 100
const bad = 15 // just a random max to enable 'bad developer' mode
const observer = new MutationObserver(function(mutationsList) {
const msg = new SpeechSynthesisUtterance()
const len = mutationsList.length
if (len > bad) {
msg.text = 'Bad Developer'
} else {
const n = len > max ? max : len
const pitch = ((n - min) / (100 - min)) * 2
msg.text = len
msg.rate = 4 - pitch * 4
msg.pitch = 2 - (pitch * 1.6 * 1.2)
}
speechSynthesis.speak(msg)
})
observer.observe(document, {
attributes: true,
childList: true,
subtree: true,
characterData: true,
})
Can anyone upload what they hear on some sites?
Hahaha, great work!
Can anyone upload what they hear on some sites?
You'll just hear a Chewbacca Jr. talking
Hahaha great idea!
Didn't work on Firefox, but Chrome is
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
Its so helpful to find out the DOM manipulations happening behind the eyes!!. Good job @R4meau
Change that stop delay on line 22 to
0.03
on Facebook and SUDDENLY I'M IN THE FUTURE.