Play a Sound with Web Audio API

Play a Sound with Web Audio API

UPDATED ON: December 20, 2014

Play a Sound with Web Audio API is a tutorial that explains some common methods of triggering and toggling buffered sounds with the Web Audio API. It will build on what we’ve covered in Web Audio API Basics and Web Audio API Audio Buffer. You should understand the basics of the Web Audio API as well as how to buffer sounds before moving on. If not, please refer to the previous tutorials. After mastering these concepts, you should be able to follow along.

So, I’ve compiled various methods of triggering and toggling sounds with the Web Audio API. We’ll use a few simple scenarios of accessing and controlling buffered audio assets to detail these methods. We’ll also cover how to create start and stop functions. But first, let’s talk about Event Listeners.

Play a Sound with Web Audio API

Event Listeners

The easiest way to trigger a sound is on page load. However, this technique is not very practical for most applications. Can you imagine having to reload the web page every time you wanted to hear a sound? For all of the scenarios in this tutorial, we’ll have some kind of interface bound to a JavaScript Event Listener that changes a parameter or runs a function in response to signals sent from the interface. First, let’s break them down by the type of Event Listener, starting with the most important. Then we’ll look at some different scenarios for each one.

Mouse Event

The Mouse Event Listener “listens” for a signal from the mouse. This can be triggered by mousedown, mouseup, click, dblclick, mousemove, mouseover and mouseout. Clicking on the interface element or alternatively, hovering the pointer over the interface element are the two most common ways to initiate an action in a web browser. These are the two methods people are most accustomed to using.

<dl>
<dt>click</dt>
<dd>The click event occurs when the pointing device button is clicked over an element. A click is defined as a mousedown and mouseup over the same screen location. The sequence of these events is:
<ol>
<li>mousedown</li>
<li>mouseup</li>
<li>click</li>
</ol>
</dd>
<dt>mousedown</dt>
<dd>The mousedown event occurs when the pointing device button is pressed over an element.</dd>
<dt>mouseup</dt>
<dd>The mouseup event occurs when the pointing device button is released over an element.</dd>
<dt>mouseover</dt>
<dd>The mouseover event occurs when the pointing device is moved onto an element.</dd>
<dt>mousemove</dt>
<dd>The mousemove event occurs when the pointing device is moved while it is over an element.</dd>
<dt>mouseout</dt>
<dd>The mouseout event occurs when the pointing device is moved away from an element.</dd>
</dl>

Keyboard Event

The Keyboard Event Listener “listens” for a signal from the computer keyboard. This can be triggered in three different ways. KeyDown happens when a key is first pressed down. KeyPress happens when a key is pressed down and then released. KeyUp happens when the key is released. All three events occur in this order when a key is pressed and then released.

<dl>
<dt>keydown</dt>
<dd>The keydown event occurs when the key is pressed.</dd>
<dt>keypress</dt>
<dd>The keypress event occurs when the key is pressed, after the keydown event.</dd>
<dt>keyup</dt>
<dd>The keyup event occurs when the key is released.</dd>
</dl>

Other Ways to Trigger Sounds

Besides Mouse and Keyboard Events, there are several other ways to trigger or play a sound. Of course, touchscreen devices make use of Touch Events. With the recent growth of smart phones and other mobile devices, this one will surely gain importance in the future. The Web Audio API gives us the ability to fire off sounds at timed intervals with a high degree of accuracy. Also we have the ability to append or concatenate AudioBuffers and even loop an entire, or specific portions of an AudioBuffer. A related API called the Web MIDI API helps us use music devices, such as synthesizers, keyboard controllers, and drum machines to trigger sounds. I’ll probably cover each of these topics in a tutorial in the near future, but for now, let’s settle for Mouse and Keyboard Events.

Start and Stop Functions

Before getting into the applied scenarios, let’s think about the functions that will be called by our Event Listeners. We need a start function that runs when the user activates the play interface. We also need a stop function that runs when the user activates the stop interface. The stop function is easy so we’ll start with that one.

Stop Function

We create a function called stop. This function, when activated, tells the buffered audio source to stop immediately.

function stop() {
    source.stop(context.currentTime); // stop the source immediately
}

UPDATE: As of July 2014, source.stop() should be used instead of source.noteOff().

Start Function

The start function can be a bit trickier. In order to allow for flexibility later, we’ll take a more advanced approach and actually create three different functions to handle this. First of all, the start function asynchronously reads an audio file into an arraybuffer as binary data. It then passes the data to a function called audioRouting.

// Load the Sound with XMLHttpRequest
function start() {
    // Note: this will load asynchronously
    var request = new XMLHttpRequest();
    request.open("GET", url, true);
    request.responseType = "arraybuffer"; // Read as binary data

    // Asynchronous callback
    request.onload = function() {
        var data = request.response;

        audioRouting(data);
    };

    request.send();
}

The audioRouting function creates a sound source, creates a source buffer from the raw binary, adds the buffered data to the source, connects the sound source to the output, and finally passes the source to the playSound function.

// Create Buffered Sound Source
function audioRouting(data) {
    source = context.createBufferSource(); // Create sound source
    context.decodeAudioData(data, function(buffer){ // Create source buffer from raw binary
    source.buffer = buffer; // Add buffered data to object
    source.connect(context.destination); // Connect sound source to output
    playSound(source); // Pass the object to the play function
    });
}

UPDATE: As of July 2014, context.decodeAudioData(data, function(buffer){}); should be used instead of buffer = context.createBuffer(data, true);.

The playSound function simply tells the buffered audio source to play immediately.

// Tell the Source when to play
function playSound() {
    source.start(context.currentTime); // play the source immediately
}

UPDATE: As of July 2014, source.start() should be used instead of source.noteOn().

Applied Scenarios

To play a sound with Web Audio API, it must be buffered first. If a buffered sound is very short and isn’t being looped, there’s probably no need for a stop function. However, if a buffered sound is longer than a few seconds or looped, please consider the sanity of your users and include a stop function. Because the sample we’re using for all the buffered sound scenarios is a bit longer, we’ve included stop functions.

Speaking of sanity, I’d like to add one quick note about the simple functions used in this tutorial. There’s nothing to prevent the sound from being triggered rapidly multiple times, but the stop function will only stop the first occurrence! This is not really a glitch or a bug. It’s just me being lazy. The Web Audio API was designed to allow this behavior. After all, it could be useful. It’s not useful to us in this tutorial though. To make it smarter, we could have it check to see if the sound is currently playing, and only trigger the sound again if it’s not currently playing. More on that in the future. Please just be aware that if things get too crazy, you can always stop the sound by reloading or refreshing the page in your browser.

Click real button elements to trigger or stop the buffered sound

Play a Sound with Web Audio API

In this scenario, our buffered sound can be activated by clicking with the mouse on the play button. It can be stopped by clicking on the stop button. By using real button elements we can ensure better accessibility.


document.querySelector('.play-button').addEventListener('click', start);
document.querySelector('.stop-button').addEventListener('click', stop);

Click images to trigger or stop the buffered sound

Play a Sound with Web Audio API

Again, our buffered sound can be activated by clicking with the mouse on the play button. This time however, the play button is actually an image element. While not as good for accessibility as the button element, the image element is bit more flexible. We could have created fancy graphics for our controls and set them as the image source. Instead, we’ve created custom shapes with CSS that give visual cues to the function of each image. One benefit of using CSS shapes instead of actual images is reduced load time for the page.


document.querySelector('.play').addEventListener('click', start);
document.querySelector('.stop').addEventListener('click', stop);
.play {
    display:inline-block;
    margin:5px 20px 5px 10px;	
    width: 0;
    height: 0;
    border-left: 50px solid #444;
    border-top: 25px solid transparent;
    border-bottom: 25px solid transparent;
	text-indent: -200px;
}

.stop {
    display:inline-block;
	background: #444;
    margin:5px;	
    width: 50px;
    height: 50px;
	text-indent: -200px;
}

Mouseover paragraphs to trigger or stop the buffered sound

Play a Sound with Web Audio API

Sometimes it might be desirable to have the sound play when a user hovers over an element instead of clicking on it. Here, we use the paragraph element to show that these techniques can be used on elements besides buttons and images. Whatever element is used, make sure it gives the user visual cues to ensure ease of use. In this case, the paragraphs are color coded and include instructional text. Also, because the start and stop functions are triggered upon mouseover, the color of each paragraph changes to yellow when the mouse is hovered over it.

Hover your mouse pointer over this paragraph to PLAY the buffered sound.

Hover your mouse pointer over this paragraph to STOP the buffered sound.

document.querySelector('.play2').addEventListener('mouseover', start);
document.querySelector('.stop2').addEventListener('mouseover', stop);

Press separate QWERTY keyboard keys to trigger and stop the buffered sound

A buffered sound can also be activated by pressing keys on a QWERTY keyboard. Here, we’d like to activate the sound by pressing [Q] and stop the sound by pressing [W]. This can be achieved by using the keydown Event Listener and the proper keyCodes for the letter Q and the letter W.

$(document).on("keydown", function(e) {
     console.log(e);
     switch (e.keyCode) {
          case 81: // Q
               start();
          break;
          case 87: // W
               stop();
          break;
     }
})

Press and release a single QWERTY keyboard key to trigger and stop the buffered sound

This time, we’d like to start and stop the buffered sound with only one key. We’ll activate the sound by pressing down [F] and stop the sound by releasing [F]. Again, we use the keydown Event Listener. This time we use the keyCode for the letter F to call the start function.

$(document).on("keydown", function(e) {
     console.log(e);
     switch (e.keyCode) {
          case 70: // F
               start();
          break;
     }
})

We use the keyup Event Listener with the keyCode for the letter F to call the stop function.

$(document).on("keyup", function(e) {
     console.log(e);
     switch (e.keyCode) {
          case 70: // F
               stop();
          break;
     }
})

Play a Sound with Web Audio API

Well, there you have it. That should be more than enough to get you going. I’ve shown you a bunch of different ways to play a sound with Web Audio API. Now it’s up to you to implement them in your next project. How will you use these techniques to make the web awesome? Please drop me a comment below if you have any questions about these methods.

Other tutorials in this series

More examples of the Web Audio API in action


Comments

13 responses to “Play a Sound with Web Audio API”

  1. Joseph Johnston Avatar
    Joseph Johnston

    I really love these tutorials. Please, keep them coming!

    1. Thanks Joseph! I’m glad to be helpful. I’ve got more Web Audio Tutorials in the works for 2014. Stay tuned!

  2. BumbleBuzz Avatar
    BumbleBuzz

    Hi, great tutorial, BUT the example isn’t playing any sound for me in chrome? I have the newest one and other web audio apps work in it, I can only get it to work in safari, any news on this?

    Cheers 😀

    1. The tutorial has been updated to match the new spec. Sorry for the inconvenience. It should work fine now!

  3. Thanks for great tutorial.
    I had a problem with this one. This code is not executed in chrome and firefox. It gives this error:
    Failed to execute ‘createBuffer’ on ‘AudioContext’: 3 arguments required, but only 2 present.

    Then I changed audiRouting function to this:

    function audioRouting(data){
    source = context.createBufferSource();
    // Create sound source
    // buffer = context.createBuffer(data,true);
    context.decodeAudioData(data, function(buffer){
    // Create source buffer from raw binary
    source.buffer = buffer;
    // Add buffered data to object
    source.connect(context.destination);
    // Connect sound source to output
    playSound(source);
    // Pass the object to the play function
    });

    }

    And now it works

    1. Thanks for pointing this out Ehsan! It’s now fixed. Must use the decodeAudioData method instead of createBuffer method. Cheers!

  4. I have a question too.
    I have written a function to play different audio files when a key is pressed
    Of course it works for each key one audio file is played but when I press two keys at the same time it does not play both sounds.
    Playing two sounds at the same time is necessary in music. How could I do that?

    1. I am sorry I have not seen this article. You have explained how to do this job here:
      https://middleearmedia.com/web-audio-api-bufferloader/

      I used that and play two sound at the same time. The only problem is quality of the sounds

      I want to make a piece of music with audio api. Should I stop each note after it has been played? What should I consider for the best quality of music which is played with audio api?

      1. try adding a compressor node to clean up the sound if it seams glitchy.

  5. Is it possible to play a music sheet with web audio api?
    I think maybe it could be done in this way
    Make an array of notes and then play each of them at a specific time
    But should the notes would be buffered once or many times when the piece is played?

  6. Morten Skogly Avatar
    Morten Skogly

    If I press and hold F on https://middleearmedia.com/demos/webaudio/playsoundbuffer.html it starts the sound over and over again hundreds of times. How to prevent that?

  7. Carlos Arturo Avatar
    Carlos Arturo

    The playSound function is missing the sound source:
    function playSound(source) { //instead of playSound()
    source.start(context.currentTime); // play the source immediately
    }
    This tutorial rocks!!! Can’t wait for the Web Midi API tutorial, please!! =)

Leave a Reply

Your email address will not be published. Required fields are marked *