Wad is a Javascript library for manipulating audio using the new HTML5 Web Audio API. It greatly simplifies the process of creating, playing, and manipulating audio, either for real-time playback, or at scheduled intervals. Wad provides a simple interface to use many features one would find in a desktop DAW (digital audio workstation), but doesn't require the user to worry about sending XHR requests or setting up complex audio graphs.
To see a demo of an app that uses a small subset of the features in Wad.js, check this out.
To use Wad.js in your project, simply include the script in your HTML file.
<script src="path/to/build/wad-min.js"></script>
Wad.js is also available as a bower package.
bower install wad
The simplest use case is loading and playing a single audio file.
var bell = new Wad({source : 'http://www.myserver.com/audio/bell.wav'})
bell.play()
bell.stop()
Behind the scenes, Wad sends an XMLHttpRequest to the source URL, so you will need a server running to respond to the request. You can't simply test it with local files, like you can with an HTML <audio> tag.
You can also create oscillators using the same syntax, by specifying 'sine', 'square', 'sawtooth', or 'triangle' as the source.
var saw = new Wad({source : 'sawtooth'})
The peak volume can be set during the creation of a wad, or any time afterwards. The default value is 1.
var saw = new Wad({source : 'sawtooth', volume : .9})
saw.setVolume(0.5)
The Wad constructor supports many optional arguments to modify your sound, from simple settings such as peak volume, to more powerful things like ADSR envelopes and filters. If not set explicitly, the ADSR envelope will have the values shown below. Filters, LFOs, and reverb are not used unless they are set explicitly. Filter type can be specified as either 'lowpass', 'highpass', 'bandpass', 'lowshelf', 'highshelf', 'peaking', 'notch', or 'allpass'.
var saw = new Wad({
source : 'sawtooth',
volume : 1.0, // Peak volume can range from 0 to an arbitrarily high number, but you probably shouldn't set it higher than 1.
pitch : 'A4', // Set a default pitch on the constuctor if you don't want to set the pitch on play()
.
panning : -5, // Horizontal placement of the sound source. Sensible values are from 10 to -10.
env : { // This is the ADSR envelope.
attack : 0.0, // Time in seconds from onset to peak volume. Common values for oscillators may range from 0.05 to 0.3.
decay : 0.0, // Time in seconds from peak volume to sustain volume.
sustain : 1.0, // Sustain volume level. This is a percent of the peak volume, so sensible values are between 0 and 1.
hold : 9001, // Time in seconds to maintain the sustain volume level. If this is not set to a lower value, oscillators must be manually stopped by calling their stop() method.
release : 0 // Time in seconds from the end of the hold period to zero volume, or from calling stop() to zero volume.
},
filter : {
type : 'lowpass', // What type of filter is applied.
frequency : 600, // The frequency, in hertz, to which the filter is applied.
q : 1, // Q-factor. No one knows what this does. The default value is 1. Sensible values are from 0 to 10.
env : { // Filter envelope.
frequency : 800, // If this is set, filter frequency will slide from filter.frequency to filter.env.frequency when a note is triggered.
attack : 0.5 // Time in seconds for the filter frequency to slide from filter.frequency to filter.env.frequency
}
},
reverb : {
wet : 1, // Volume of the reverberations.
impulse : 'http://www.myServer.com/path/to/impulse.wav' // A URL for an impulse response file, if you do not want to use the default impulse response.
},
vibrato : { // A vibrating pitch effect. Only works for oscillators.
shape : 'sine', // shape of the lfo waveform. Possible values are 'sine', 'sawtooth', 'square', and 'triangle'.
magnitude : 3, // how much the pitch changes. Sensible values are from 1 to 10.
speed : 4, // How quickly the pitch changes, in cycles per second. Sensible values are from 0.1 to 10.
attack : 0 // Time in seconds for the vibrato effect to reach peak magnitude.
},
tremolo : { // A vibrating volume effect.
shape : 'sine', // shape of the lfo waveform. Possible values are 'sine', 'sawtooth', 'square', and 'triangle'.
magnitude : 3, // how much the volume changes. Sensible values are from 1 to 10.
speed : 4, // How quickly the volume changes, in cycles per second. Sensible values are from 0.1 to 10.
attack : 0 // Time in seconds for the tremolo effect to reach peak magnitude.
}
})
If you've used other audio software before, you probably know what most of these settings do, though panning works a little bit differently. With Web Audio, you don't directly set the left/right stereo balance. Rather, the panning setting describes the distance of the sound source from the audio listener, along the X axis. You can set the panning to arbitrarily high or low values, but it will make the sound very quiet, since it's very far away.
Wad.js supports 3D panning. Any time you would pass in a panning parameter (either to the constructor, the play()
method, or the setPanning()
method), you can pass it in as a three element array to specify the X, Y, and Z location of the sound.
var saw = new Wad({
source : 'sawtooth',
panning : [0, 1, 10]
})
The filter constructor argument can be passed an object or an array of objects. If an array is passed, the filters are applied in that order. Whichever form is passed to the constructor should also be passed to the play argument.
filter: [
{type : 'lowpass', frequency : 600, q : 1, env : {frequency : 800, attack : 0.5}},
{type : 'highpass', frequency : 1000, q : 5}
]
In order to use reverb, you will need a server to send an impulse response via XmlHttpRequest. An impulse response is a small audio file, like a wav or mp3, that describes the acoustic characteristics of a physical space. By default, Wad.js serves a sample impulse response that you can use freely. However, it is recommended that you use your own impulse response. To use your own impulse response, pass a URL to an impulse response file as an argument to the constructor, as shown above. You can also modify the attribute Wad.defaultImpulse to change the default impulse response. You can make your own impulse response, but it might be easier to just find one online.
If you want to use reverb on many Wads simultaneously, you may run into performance issues. You can work around this problem by using global reverb, which uses the same convolver node for many Wads, rather than using a seperate convolver node for each Wad that uses reverb. To use global reverb, there are two steps. First, call Wad.setGlobalReverb()
.
Wad.setGlobalReverb({
wet : 1, // Volume of the reverberations.
impulse : 'http://www.myServer.com/path/to/impulse.wav' // A URL for an impulse response file, if you do not want to use the default impulse response.
})
Next, set globalReverb : true
when creating a new Wad.
var sine = new Wad({source : 'sine', globalReverb : true})
You can also set or unset global reverb on a Wad after it's been created.
sine.globalReverb = false
The play()
method also accepts optional arguments: volume, wait, pitch, envelope, panning, and filter. If you intend to include a filter envelope or panning as an argument on play()
, you should have set a filter envelope or panning when the Wad was first instantiated. Pitches can be named by the note name, followed by the octave number. Possible values are from A0 to C8. Sharp and flat notes can be named enharmonically as either sharps or flats (G#2/Ab2), but don't try to be pedantic. There is no mapping for C## or Fb. Check the Wad.pitches attribute for a complete mapping of note-names to frequencies.
var saw = new Wad({source : 'sawtooth'})
saw.play({
volume : 0.8,
wait : 0, // Time in seconds between calling play() and actually triggering the note.
pitch : 'A4', // A4 is 440 hertz.
label : 'A', // A label that identifies this note.
env : {hold : 9001},
panning : [1, -1, 10],
filter : {frequency : 900}
})
If you like, you can also select a pitch by frequency.
saw.play({pitch : 440})
When you call stop()
on a Wad, it will only stop the most recently triggered note. If you want to retain control over multiple notes that played from the same Wad, you can label those notes when play()
is called. When stop()
is called, you can pass in a label argument to stop all currently sustained notes with that label.
saw.play({pitch : 'A4', label : 'A4'}) // The label can be any string, but using the same name as the note is often sensible.
saw.play({pitch : 'G4', label : 'G4'})
saw.stop('A4') // The first note will stop, but the second note will continue playing.
If you want to change an attribute of a Wad during playback, you can use the relevant setter method for that attribute.
saw.play()
saw.setPanning(-2)
You can also use microphone input as the source for a Wad. You can apply reverb or filters to the microphone input, but you cannot apply an envelope or filter envelope. If a Wad uses the microphone as the source, it will constantly stream the mic input through all applied effects (filters, reverb, etc) and out through your speakers or headphones as soon as you call the play()
method on that Wad. Call the stop()
method on a microphone Wad to disconnect your microphone from that Wad. You may experience problems with microphone feedback if you aren't using headphones.
var voice = new Wad({
source : 'mic',
reverb : {
wet : .4
}
filter : {
type : 'highpass',
frequency : 700
},
panning : -2
}
voice.play()
Sometimes you might want to incorporate external libraries into Wad, for example FX or visualizers. You can override the constructExternalFx and setUpExternalFxOnPlay methods to add those nodes to the wad chain. In the following example the values are hardcoded, but they could easily have been passed as arguments to play.
//For example to add a Tuna chorus you would put this somewhere in your own code, and also include the Tuna library:
var tuna;
Wad.prototype.constructExternalFx = function(arg, context){
this.tuna = new Tuna(context);
this.chorus = arg.chorus
}
Wad.prototype.setUpExternalFxOnPlay = function(arg, context){
var chorus = new tuna.Chorus({
rate: arg.chorus.rate || this.chorus.rate,
feedback: arg.chorus.feedback || this.chorus.feedback,
delay: arg.chorus.delay || this.chorus.delay,
bypass: arg.chorus.bypass || this.chorus.bypass
});
chorus.input.connect = chorus.connect.bind(chorus)//we do this dance because tuna exposes its input differently.
that.nodes.push(chorus.input)//you would generally want to do this at the end unless you are working with something that does not modulate the sound (i.e, a visualizer)
}
If you'd like to use a pre-configured Wad, check out the presets. They should give you a better idea of the sorts of sounds that you can create with Wad.js. For example, you can create a Wad using the preset 'hiHatClosed' like this:
var hat = new Wad(Wad.presets.hiHatClosed)
Wad.js can read MIDI data from MIDI instruments and controllers, and you can set handlers to respond to that data. MIDI handler functions are stored in the Wad.midiMaps
array, which should have one handler function for each MIDI device you are using. By default, Wad.midiMaps[0]
is configured to log MIDI messages to the console, which should be sufficient if you are only testing one device. If you want to quickly set up a MIDI keyboard to play a Wad, assign a Wad of your choice (or any object with play()
and stop()
methods) to Wad.midiInstrument
.
Wad.midiInstrument = new Wad({source : 'sine'})
If you want to use multiple MIDI devices at once, or you'd like to get creative with how Wad.js handles MIDI data, I strongly encourage you to write your own MIDI handler functions. For example, note-on velocity (how hard you press a key when playing a note) usually modulates the volume of a note, but it might sound interesting if you configure note-on velocity to modulate the attack or filter frequency instead. You could configure the right half of your keyboard to play a guitar, and configure the left half of your keyboard to play a bass. If you want to take that a step further, you can use a sustain pedal to toggle between slap and pop sounds on the bass, if you're into that style of music. Or maybe you'd like to map the lowest octave on your keyboard to a drum kit, and use a sustain pedal to play the kick-drum. You can do almost anything, if you're clever. Wad.js simply maps MIDI data to function calls, so your MIDI device can do anything that you can accomplish with Javascript. You can send MIDI data through websockets for some kind of WAN concert, or set up a Twitter bot that automatically tells your friends what key you've been playing in. If you can design a really cool and creative MIDI rig, I'd love to hear about it, and might include it in Wad.js.
Wad.midiMaps[0] = function(event){
console.log(event.receivedTime, event.data)
}
Wad.midiMaps[1] = anotherMidiHandlerFunction // If you have multiple MIDI devices that you would like to use simultaneously, you will need multiple MIDI handler functions.
I've put a lot of work into this project, but there's still plenty of room for improvement, both in terms of bugfixes and feature additions. Please feel free to fork this repo and submit pull requests.
I tried to future-proof Wad.js by using standards-compliant methods, but the cross-browser compatibility is still not great. It works best in Chrome, decently in Safari for iOS, and it works very poorly in Firefox. I have not tested it in any other browsers. I would greatly appreciate contributions to help Wad.js run optimally in any browser that supports Web Audio, especially mobile browsers.
Originally, I had wanted to allow users to easily add an LFO to any parameter, such as pitch, volume, panning, resonance, filter cutoff frequency, etc, but this turned out to be fairly difficult for me to implement. Instead, I implemented LFOs specifically for pitch (vibrato) and volume (tremolo). If anyone can implement more versatile LFOs, that would be awesome.
It would be nice if there were more presets, so that users wouldn't have to make most of their sounds from scratch. If you enjoy making your own sounds with Wad.js, consider submitting them to be used as presets. Better yet, you can bundle together a bunch of presets as a 'preset-pack'.