Skip to content

Instantly share code, notes, and snippets.

@johan--
Forked from MarkArts/plink-plonk.js
Created February 19, 2020 13:26
Show Gist options
  • Save johan--/ae79bfbe907f81cffa0f1cb9b68b5944 to your computer and use it in GitHub Desktop.
Save johan--/ae79bfbe907f81cffa0f1cb9b68b5944 to your computer and use it in GitHub Desktop.

Revisions

  1. @MarkArts MarkArts revised this gist Feb 18, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions plink-plonk.js
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    // 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
  2. @MarkArts MarkArts revised this gist Feb 18, 2020. 1 changed file with 95 additions and 9 deletions.
    104 changes: 95 additions & 9 deletions plink-plonk.js
    Original file line number Diff line number Diff line change
    @@ -2,29 +2,115 @@
    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(function(mutationsList) {
    const oscillator = audioCtx.createOscillator()
    const observer = new MutationObserver(observe)

    function observe(mutationsList) {
    // with delay
    delayNote(gain => playNote(mutationsList, gain), 300, 0.2)
    // without
    // playNote(mutationsList)
    }


    oscillator.connect(audioCtx.destination)
    oscillator.type = "sine"
    // 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(
    Math.log(mutationsList.length + 5) * 880,
    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 + 0.01)
    })
    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,
    })
    })

  3. @tomhicks tomhicks revised this gist Feb 14, 2020. 1 changed file with 9 additions and 0 deletions.
    9 changes: 9 additions & 0 deletions plink-plonk.js
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,12 @@
    /*
    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()
  4. @tomhicks tomhicks created this gist Feb 14, 2020.
    21 changes: 21 additions & 0 deletions plink-plonk.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    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,
    })