This tutorial is designed for programmers with beginner to intermediate-level knowledge of HTML, CSS, and Javascript

Making a web app that utilizes in-browser reverb:

This tutorial takes advantage of the Web Audio API’s Convolution Reverb.

This is a great tutorial for anyone who has some Javascript and audio experience, but is looking into how to utilize the two together. The Web Audio API, maintained by Mozilla, is (to be frank) not the most intuitively written API out there. I wrote this tutorial to give audio-interested programmers a way to get their toes wet in Web Audio.

Convolution reverb simulates reverberation by placing performing a mathematical operation on the source audio file using a recording of the Impulse Response, which illustrates how a sound travels through a physical space. Here's a link to a YouTube video that explains the process in greater detail: Convolution Reverb Explained

This is what we'll be building today

Here are the steps we'll go through:

  1. Create a seperate folder for this project. In it, create an index.html file and a folder called 'assets'. Download the assets from this link: Assets and put them in your assets folder.

  2. Then we start with a blank HTML page like so:
  3. <html>
        <head>
            <title>Convolution Reverb App</title>
        </head>
        <body>
        </body> 
    </html> 
  4. Next we’ll add some elements and buttons to our body:
  5. <body>
    <div id="asdf">
        <div><p id="a">Press 'a' for kick</p></div>
        <div><p id="s">Press 's' for snare</p></div>
        <div><p id="d">Press 'd' for hat</p></div>
        <div><p id="f">Press 'f' for clap</p></div>
    </div>
    <div id="impulses">
        <button type="button" onclick="boom()">boom</button>
        <button type="button" onclick="echo()">echo</button>
        <button type="button" onclick="space()">space</button>
        <button type="button" onclick="hall()">hall</button>
    </div>
    </body>
  6. Okay, now let’s get into some javascript. At the start of the body, we’ll include this very helpful buffer loader script by Github User Shinnn. Download and add the file to the same folder as our html file. Then include the following script:
  7. <script type="text/javascript" src="buffer-loader.js"></script>
  8. Now, we’ll write our own script below the one we just loaded. Let’s start off with our variables, we’ll initialize a context (required for running audio on a website, our bufferLoader, and our impulse response:
  9. var context; //audio context
    var bufferLoader;
    var impulseResponse = "./assets/impulse1.wav" //set default IR
  10. Next, we’ll write our impulse function, which will initialize our convolution reverb. This is the real meat of our project. Everything else we write in the project (loading our sounds and impulse responses, etc.) will be connecting to this. In this function, we will start a new audio context and use web audio api’s createConvolver function. We’re also using an ajax request to load our impulse response file. We'll also set up the convolvers buffer and connect it to the audio context. Take the time to write the section line by line so as to more fully understand what's going on in this function:
  11. function getImpulse() {
        context = new AudioContext(); //initializes audio context
        convolver = context.createConvolver();
        console.log("convolver Started")
        ajaxRequest = new XMLHttpRequest(); //requests sound from server
        ajaxRequest.open('GET', impulseResponse, true);
        console.log(impulseResponse)
        ajaxRequest.responseType = 'arraybuffer';
    
        ajaxRequest.onload = function() {
            var impulseData = ajaxRequest.response;
    
            context.decodeAudioData(impulseData, function(buffer) {
                myImpulseBuffer = buffer;
                convolver.buffer = myImpulseBuffer;
                convolver.loop = false;
                convolver.normalize = true;
                convolver.connect(context.destination);
    
            }, function(e){"Error with decoding audio data" + e.err});
        }
        ajaxRequest.send();
    }
    getImpulse();
  12. Next, we’ll write functions for each of our source audio files, using the bufferLoader that we included. The rest of this project will be comparatively much more simple:
  13. function loadAndPlayKick() {
        bufferLoader = new BufferLoader(
            context,
            [
            "./assets/kick.wav",
            ],
            finishedLoadingKick
        );
        bufferLoader.load();
    }
    function loadAndPlaySnare() {
        bufferLoader = new BufferLoader(
            context,
            [
            "./assets/snare.wav",
            ],
            finishedLoadingSnare
        );
        bufferLoader.load();
    }
    function loadAndPlayHat() {
        bufferLoader = new BufferLoader(
            context,
            [
            "./assets/hat.wav",
            ],
            finishedLoadingHat
        );
        bufferLoader.load();
    }
    function loadAndPlayClap() {
        bufferLoader = new BufferLoader(
            context,
            [
            "./assets/clap.wav",
            ],
            finishedLoadingClap
        );
        bufferLoader.load();
    }
  14. Next, we’ll add our interactivity. We will use the keyCode function onkeydown to call our corresponding functions:
  15. document.body.onkeydown = function() {keyCode(event)};
    
    function keyCode(event) {
        var x = event.keyCode;
        if (x == 65) {
            loadAndPlayKick(); //trigger kick
        }
        if (x == 83) {
            loadAndPlaySnare();
        }
        if (x == 68) {
            loadAndPlayHat();
        }
        if (x == 70) {
            loadAndPlayClap();
        }
    }
  16. To avoid errors where the file has yet to load, here is where we have the ‘finishedLoading’ functions that will start our convolution reverb using the corresponding sound.
  17. function finishedLoadingKick(bufferList) {
        var snd1 = context.createBufferSource();
        snd1.buffer = bufferList[0];
        snd1.connect(convolver);
        snd1.start(0);
    }
    function finishedLoadingSnare(bufferList) {
        var snd2 = context.createBufferSource();
        snd2.buffer = bufferList[0];
        snd2.connect(convolver);
        snd2.start(0);
    }
    function finishedLoadingHat(bufferList) {
        var snd3 = context.createBufferSource();
        snd3.buffer = bufferList[0];
        snd3.connect(convolver);
        snd3.start(0);
    }
    function finishedLoadingClap(bufferList) {
        var snd4 = context.createBufferSource();
        snd4.buffer = bufferList[0];
        snd4.connect(convolver);
        snd4.start(0);
    }
  18. Finally, we’ll allow users to switch between different impulse responses. by using these following functions.
  19. function boom(){
        impulseResponse="./assets/impulse1.wav";
        getImpulse(); //changes impulse response
    }
    function echo(){
        impulseResponse="./assets/impulse2.wav";
        getImpulse();
    }
    function space(){
        impulseResponse="./assets/impulse3.wav";
        getImpulse();
    }
    function hall(){
        impulseResponse="./assets/impulse4.wav";
        getImpulse();
    }

There are many different resources you can find online of different impulse responses. I’ve included the download to the four I used for this project in the assets folder above, but feel free to find your own to use! The same goes for the included source sounds.

Finally, because of the usage of a file system, we need to serve this app on a server. You can do this by opening up your terminal, navigating to the folder you’ve stored the project in, and writing the following code:

python -m SimpleHTTPServer 5000
.

Then you’ll be able to navigate to:

localhost:5000/index.html

and see the working script. Alternatively, you can host it on a website and it should work perfectly.

If you'd like to find other impulse responses, there are plenty of free online resources for that. Here's an example of one: OpenAir.

If you'd like to download the full project (though I advise against this) you can do so here: Full Project Download


Thank you for following this tutorial, I hope you enjoyed it!

Feel free to contact me at lucas@lucwhite.com with any and all questions you have!