Controlling Web Audio API Oscillators

Controlling Web Audio API Oscillators

UPDATED ON: December 20, 2014

Controlling Web Audio API Oscillators is a tutorial that explains some common methods of triggering and toggling oscillators with the Web Audio API. It will build on what we’ve covered in Web Audio API Basics and Web Audio API Oscillators. You should understand the basics of the Web Audio API as well as how to create oscillators, declare their waveforms, and set their frequencies before moving on. If not, please refer to the previous tutorials. After familiarizing yourself with these concepts, you should be able to follow along as I explain in detail how controlling Web Audio API oscillators can be accomplished.

Controlling Web Audio API Oscillators

DOM Events, jQuery and JavaScript EventListeners

It’s useful to understand a little about events and listeners if we want to make anything exciting happen. HTML Document Object Model (DOM) Events must be harnessed to create useful interfaces. The DOM Level 2 Event Model provides standard modules of events for user interface control and document mutation notifications. For controlling Web Audio API Oscillators, we’ll use JavaScript (or jQuery) event listeners just like we did with buffered audio in the Play a Sound with Web Audio API tutorial. These event listeners will tell our script when to run the start and stop functions.

The Click Event

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:

  1. mousedown
  2. mouseup
  3. click

Keyboard Events

A keyboard event occurs when a key is pressed or released on a (QWERTY) keyboard. There are three different keyboard events. The keydown event occurs when the key is pressed. The keypress event also occurs when the key is pressed, after the keydown event. The keyup event occurs when the key is released. The sequence of these events is:

  1. keydown
  2. keypress
  3. keyup

Bind Events Inline

So, how should we handle our events? One way to accomplish our task is to bind the events inline in the HTML. It’s not the best way, but it’s simple enough. To execute a function when a button is clicked, we can use the onClick attribute. This gives us the ability to insert JavaScript right into the button.

<element onClick="functionName">

Event Listeners Listen Well

A better, more modern way is to use event listeners. This is the unobtrusive way of handling events. Also, you can register more than one event listener for the same target. So, we listen for an event. When we hear it, we fire off one of our functions in response. First, we target our element with a unique ID.

<div id="myElement">Play</div>

Then we add an event listener for clicks on any element with the unique ID. With JavaScript, it might look like this:

document.getElementById('myElement').addEventListener('click', function() {
    functionName();
});

Or we could use jQuery, the popular JavaScript library to simplify it a little. With jQuery, our event listener might look something like this:

$('#myElement').click(function(){
	functionName();
});

Web Audio Containers, Variables and Functions

Just as we’ve learned in previous tutorials, we must first create an AudioContext. This acts as the audio container for our audio graph. All of our variables and functions will exist within the AudioContext. Speaking of variables, we’ll need some of those. Controlling Web Audio API Oscillators could mean anything from setting the frequency to changing the wave form. For our purposed here, we are concerned primarily with methods of starting and stopping them. We’ll create two special functions that will help us make this happen.

Initialize the Audio Context

As of this writing, Web Audio API only works in Blink/WebKit-based browsers such as Chrome and Safari. Therefore, the use of the “webkit” prefix is recommended. Mozilla is very close to implementing the Web Audio API into Firefox, so we should be seeing wider browser support very soon.

var context = new webkitAudioContext(); // Create audio container with webkit prefix

Declare Variables

We’ll declare our variables in the global scope so they can be disconnected. In this case, we declare the variables oscillator and gain.

var oscillator, gain;

Start Function

We’ll create a start function that can be recalled each time we want to trigger the oscillator. Our start function, called startOsc will create a sine wave oscillator and gainNode. The oscillator’s frequency is passed to the start function from the input button. The oscillator is routed through the gainNode and then to the output where it plays back instantly. You may be asking “Why is the oscillator being created again each time it’s triggered?” Simply because the Web Audio API was designed this way. More on this in a bit.

// Create function that routes an OscillatorNode through a GainNode and then to the output
function startOsc(frequency){ // Frequency is passed to this function from input button 

	// Create OscillatorNode
	oscillator = context.createOscillator(); // Create sound source
	oscillator.type = 0; // Sine wave
	oscillator.frequency.value = frequency; // Frequency in hertz (passed from input button)
	oscillator.start(0); // Play oscillator instantly
	
	// Create GainNode	
	gain = context.createGain(); // Create gain node
	gain.gain.value = 1; // Set gain to full volume

	// Connect the Nodes
	oscillator.connect(gain); // Connect oscillator to gain
	gain.connect(context.destination); // Connect gain to output

};

Stop Function

Of course, we’ll also need a way to tell the oscillator to stop playing. Our stop function, called off will stop the oscillator and disconnect it. After calling the noteOff() stop() method, the oscillator is no longer usable. It must be created again to be played again. This seems counter-intuitive, and can be a little confusing for newcomers to the Web Audio API, but actually makes sense from the modular audio graph perspective. Now you see why we included the oscillator creation inside our play function. After stopping an oscillator, it’s always a good idea to disconnect it. By disconnecting the stopped oscillator, you’re telling the browser’s garbage collector to pick it up. Understanding these concepts are important for controlling Web Audio API oscillators properly.

function off() {
	oscillator.stop(0); // Stop oscillator after 0 seconds
    oscillator.disconnect(); // Disconnect oscillator so it can be picked up by browser’s garbage collector
}

Let’s Get to the Fun Stuff

Controlling Web Audio API Oscillators can and should be fun. Let’s take a detailed look at several different ways of doing it. Although very basic, these examples are the building blocks required for creating more advanced applications such as synthesizers. You may want to open the demo in another browser tab so you can check out each of the following methods in action as you follow along.

Controlling Web Audio API Oscillators

I’d like to add one quick note about the simple functions used in this tutorial. There’s nothing to prevent the oscillator from being triggered rapidly multiple times, but the stop function will only stop the first occurrence! To make it smarter, we could check to see if the oscillator is currently playing, and only trigger it 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 oscillator by reloading or refreshing the page in your browser.

Click input elements of type=”button” to start or stop the oscillator

In this example, we use the inline binding technique on input elements with their type attribute set to button. One for starting and another for stopping the oscillator. When clicked, the Start button calls the startOsc function and specifies a frequency of 500 hertz. The Stop button, when clicked, calls the off function.

<input type="button" class="play-button" value="Start"  onClick="startOsc(500);" />
<input type="button" class="stop-button" value="Stop"  onClick="off();" />

Click custom buttons to play or stop the oscillator

This time we use the inline binding technique on real button elements to control the oscillator. By applying CSS styles, we can customize the buttons to look however we want. When clicked, the Play button calls the startOsc function and specifies a frequency of 100 hertz. The Stop button, when clicked, calls the off function.

<button class="play" onClick="startOsc(100);" >Play</button>
<button class="stop" onClick="off();" >Stop</button>

The styles we apply will make the buttons easily recognizable to users. The Play button will be shaped like a triangle pointing to the right. The Stop button will be square shaped. They’ll both be dark gray. When hovered over, they’ll both become slightly transparent. When clicked, they flash red. These visual cues help create a better User Experience (UX).

.play {
    display:inline-block;
	margin:5px 5px 5px 10px;	
	width: 0;
	height: 0;
	border-left: 50px solid #444;
	border-top: 25px solid transparent;
	border-bottom: 25px solid transparent;
	border-right: 0;
	text-indent: -9999px;
}
.stop {
	display:inline-block;
	background: #444;
	border: 0;
	margin:5px;	
	width: 50px;
	height: 50px;
	text-indent: -9999px;
}
.play:hover, .stop:hover {
	cursor:pointer;
	opacity:0.8;
}
.play:active {
    display:inline-block;
	margin:5px 5px 5px 10px;	
	width: 0;
	height: 0;
	border-left: 50px solid #f33;
	border-top: 25px solid transparent;
	border-bottom: 25px solid transparent;
	border-right: 0;
	text-indent: -9999px;
}
.stop:active {
	background: #f33;
	cursor:pointer;
	opacity:1;
}

Click custom buttons to toggle the oscillator on or off

Here, we switch it up and use jQuery event listeners on customized button elements to toggle the oscillator on or off. The On button, when clicked, triggers our startOsc function and gives it the frequency of 440 hertz. The Off button, when clicked, triggers the off function.

<button id="onButton">On</button>
<button id="offButton">Off</button>
$('#onButton').click(function(){
	startOsc(440);
});

$('#offButton').click(function(){
	off();
});

Click color coded div elements to toggle the oscillator on or off

Now we’ll switch it up again, and use JavaScript event listeners on div elements to toggle the oscillator on or off. The divs are styled and color coded to give visual cues. Green for the On div and red for the Off div might give the user enough of a clue. However, this is not nearly as robust as the visual cues that were used in the example above. For one thing, many cultures assign different meanings to colors. We can’t assume everybody will think “go” when they see green or “stop” when they see red. Another issue is that some people may not be able to differentiate between the two colors. So, please be careful when using visual cues to inform your users of important functions. The On div, when clicked, triggers our startOsc function and gives it the frequency of 150 hertz. The Off div, when clicked, triggers the off function.

<div id="onToggle">On</div>
<div id="offToggle">Off</div>
document.getElementById('onToggle').addEventListener('click', function() {
    startOsc(150);
});

document.getElementById('offToggle').addEventListener('click', function() {
	off();;
});

Press [Q] to trigger the oscillator. Press [W] to stop the oscillator

An oscillator can also be activated by pressing keys on a QWERTY keyboard. Here, we’d like to activate the oscillator by pressing [Q] and stop it by pressing [W]. This can be achieved by using the keydown jQuery event listener and the proper keyCodes for the letter Q and the letter W. We could use keypress instead, but keydown happens a bit faster so we’ll stick with it.

// Map Keyboard Events for the play/stop functions	
$(document).on("keydown", function(e) {
	console.log(e);
	switch (e.keyCode) {
		case 81:  // Q 
			startOsc(300);
		break;
		case 87: // W 
			off();
		break;
	}
})

Press down [Z] to trigger the oscillator. Release [Z] to stop the oscillator

For this one, we’d like to start and stop the oscillator with only one key. We’ll activate it by pressing down [Z] and stop the sound by releasing [Z]. Again, we use the keydown event listener. This time we use the keyCode for the letter Z to call the startOsc function. We’ll also pass it the frequency of 82.407 hertz which is the same as the low E string on a guitar.

// Map Keyboard Events for the Z keydown 	
$(document).on("keydown", function(e) {
	console.log(e);
	switch (e.keyCode) {
		case 90: // Z
			startOsc(82.407); // Pass frequency for Low E
		break;
	}
})

We use the keyup event listener with the keyCode for the letter Z to call the off function.

// Map Keyboard Events for the Z keyup	
$(document).on("keyup", function(e) {
	console.log(e);
	switch (e.keyCode) {
		case 90: // Z
			off();
		break;
	}
})

Controlling Web Audio API Oscillators

Well, that’s it for now. These examples should be more than enough to help you get a handle on your oscillators. I’ve shown you several different ways of controlling Web Audio API oscillators. Now it’s up to you to do something totally amazing with them. How will you use these techniques to make the web awesome?

Other tutorials in this series

More examples of the Web Audio API in action


Comments

3 responses to “Controlling Web Audio API Oscillators”

  1. Hey, great tutorials, it really helped me to understand more about web audio API, i think this might be one of the best series about oscillators. I’ve noticed in these examples if you press the keys twice or the start button twice(depending on the example) the sound starts to get duplicated or something like that, maybe there should be a validation to check if the oscillator it’s already playing, anyway, thank you!

  2. Gonzales Avatar
    Gonzales

    Hello,

    nice Tutorial, but do have a solution to cut the Crackles at Start of Stop of the Oscillator.

  3. Thanks for the article! I used your examples to create a lightweight metronome at https://ticktick.info. I’ve looked around at the competition, and other online metronomes are usually Flash or Java plugin-based. 😛 . Here’s to the power of JavaScript in the browser!

Leave a Reply

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