Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Listen to your web pages
@R4meau

This comment has been minimized.

Copy link

R4meau commented Feb 14, 2020

Haha, I wonder what kind of debugging you're into. XD

@dawidjaniga

This comment has been minimized.

Copy link

dawidjaniga commented Feb 14, 2020

Great idea πŸ‘ πŸ₯‡

@vprasanth

This comment has been minimized.

Copy link

vprasanth commented Feb 14, 2020

This hilarious! thanks!

@tkesgar

This comment has been minimized.

Copy link

tkesgar commented Feb 14, 2020

This is cool stuff, thank you for sharing! πŸ‘ πŸ‘ πŸ‘

@LXSMNSYC

This comment has been minimized.

Copy link

LXSMNSYC commented Feb 15, 2020

I can never hear my websites the same way again. I need to release these souls from their agony.

@asdfx86

This comment has been minimized.

Copy link

asdfx86 commented Feb 15, 2020

Sounds beautiful, love it β™₯πŸ’—

@lenaggar

This comment has been minimized.

Copy link

lenaggar commented Feb 15, 2020

wow! I like it 😁

@mariocesar

This comment has been minimized.

Copy link

mariocesar commented Feb 15, 2020

Make a browser extensions before somebody else does. Take the credit! cool stuff!

@rippling-frontend

This comment has been minimized.

Copy link

rippling-frontend commented Feb 15, 2020

I can never hear my websites the same way again. I need to release these souls from their agony.

True that!

@antonkomarev

This comment has been minimized.

Copy link

antonkomarev commented Feb 15, 2020

Brilliant!

@m00dy

This comment has been minimized.

Copy link

m00dy commented Feb 15, 2020

very smart !!!

@SeriousM

This comment has been minimized.

Copy link

SeriousM commented Feb 15, 2020

I can never hear my websites the same way again. I need to release these souls from their agony.

True that!

And add a sound output for network activity!

@R4meau

This comment has been minimized.

Copy link

R4meau commented Feb 15, 2020

And add a sound output for network activity!

Now this!

@nessup

This comment has been minimized.

Copy link

nessup commented Feb 15, 2020

Awesome!

@dsharhon

This comment has been minimized.

Copy link

dsharhon commented Feb 15, 2020

javascript:(function plink_plonk_bookmarklet () {
  /*
  Author: Tom Hicks
  Copy this into a bookmark URL and click on it from 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
  });
})();
@Fortyseven

This comment has been minimized.

Copy link

Fortyseven commented Feb 15, 2020

Change that stop delay on line 22 to 0.03 on Facebook and SUDDENLY I'M IN THE FUTURE.

@jcraigk

This comment has been minimized.

Copy link

jcraigk commented Feb 15, 2020

This is absolutely brilliant. And useful.

@sbrichardson

This comment has been minimized.

Copy link

sbrichardson commented Feb 15, 2020

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,
})  
@makeworld-the-better-one

This comment has been minimized.

Copy link

makeworld-the-better-one commented Feb 16, 2020

Can anyone upload what they hear on some sites?

@larscmagnusson

This comment has been minimized.

Copy link

larscmagnusson commented Feb 16, 2020

Hahaha, great work!

@LXSMNSYC

This comment has been minimized.

Copy link

LXSMNSYC commented Feb 16, 2020

Can anyone upload what they hear on some sites?

You'll just hear a Chewbacca Jr. talking

@nathanvogel

This comment has been minimized.

Copy link

nathanvogel commented Feb 16, 2020

Hahaha great idea! πŸ‘ (could be a nice prank too)
Didn't work on Firefox, but Chrome is βœ”οΈ

@R4meau

This comment has been minimized.

Copy link

R4meau commented Feb 16, 2020

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.

@iAnatoly

This comment has been minimized.

Copy link

iAnatoly commented Feb 16, 2020

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.

@aflemi2

This comment has been minimized.

Copy link

aflemi2 commented Feb 16, 2020

Love this!

@iAnatoly

This comment has been minimized.

Copy link

iAnatoly commented Feb 16, 2020

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,
})  
@tatthien

This comment has been minimized.

Copy link

tatthien commented Feb 17, 2020

Genius!

@epranka

This comment has been minimized.

Copy link

epranka commented Feb 17, 2020

Nice work! Check this out https://soundcode.now.sh too πŸ™‚

@ttcremers

This comment has been minimized.

Copy link

ttcremers commented Feb 17, 2020

Absolutely wonderful! πŸ˜„

@fbedussi

This comment has been minimized.

Copy link

fbedussi commented Feb 17, 2020

awesome! My computer now sounds like one from a '70s TV serial

@soundmasteraj

This comment has been minimized.

Copy link

soundmasteraj commented Feb 17, 2020

Could replace the pulses with Rice Krispies sounds. Now, web == tasty breakfast!

@ricokahler

This comment has been minimized.

Copy link

ricokahler commented Feb 18, 2020

try this on a typing test website. very enjoyable lol

@MarkArts

This comment has been minimized.

Copy link

MarkArts commented Feb 18, 2020

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,
})  
@luigimannoni

This comment has been minimized.

Copy link

luigimannoni commented Feb 19, 2020

Deploy this on production but use an Arnold Schwarzenegger sound bank which plays a random line at each DOM mutation.

@albertodeago

This comment has been minimized.

Copy link

albertodeago commented Feb 19, 2020

Wow this is probably the best gist I've ever seen

@dodds-cc

This comment has been minimized.

Copy link

dodds-cc commented Feb 19, 2020

love this

@nanxiaobei

This comment has been minimized.

Copy link

nanxiaobei commented Feb 19, 2020

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~ πŸ˜‡

@everaldo

This comment has been minimized.

Copy link

everaldo commented Feb 19, 2020

Awesome idea!

It would be nice to create some debugging library with sounds for Ajax Requests, Ajax failures, console errors etc.

@R4meau

This comment has been minimized.

Copy link

R4meau commented Feb 19, 2020

@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.

@everaldo

This comment has been minimized.

Copy link

everaldo commented Feb 19, 2020

@R4meau, that's awesome!

@Adrinalin4ik

This comment has been minimized.

Copy link

Adrinalin4ik commented Feb 20, 2020

Maybe someone makes some chrome extension?

@AlexRatmansky

This comment has been minimized.

Copy link

AlexRatmansky commented Feb 20, 2020

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})})();
@aibolik

This comment has been minimized.

Copy link

aibolik commented Feb 21, 2020

Maybe someone makes some chrome extension?

Yeah, let's do it... (adding to backlog) πŸ˜…

@R4meau

This comment has been minimized.

Copy link

R4meau commented Feb 21, 2020

@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.

@mahnouel

This comment has been minimized.

Copy link

mahnouel commented Feb 28, 2020

Could anyone add reload support? This is so wonderful πŸ₯° Maybe via localStorage? Or as Firefox Extension?

@iamnmanoj

This comment has been minimized.

Copy link

iamnmanoj commented Mar 4, 2020

Its so helpful to find out the DOM manipulations happening behind the eyes!!. Good job @R4meau

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.