WebAudio Experiences


To play samples with the webaudi API you first need a sample source, a binary sound file in a format your browser is able to decode. The sample, in the format of an ArrayBuffer, must then be decoded by audioContext.decodeAudioData.

The resulting AudioBuffer is played by an AudioBufferSourceNode. When play has finished the node dispatches the 'ended' event, a listener function does cleanup; stops the node, and disconnects it from the output. The ArrayBuffer that is passed to the decodeAudioData method is processed and then erased. When you want to keep the buffer make a copy using buffer.slice() as in this example.

function playSample(sampleBuffer){ var context = getContext(); context.decodeAudioData(sampleBuffer.slice()).then((audioBuffer)=>{ var node = new AudioBufferSourceNode(context); node.buffer = audioBuffer; node.addEventListener('ended', ()=>{ node.stop(); node.disconnect(); }) node.connect(context.destination); node.start(); }).catch(()=>{alert('error decoding sample data')}) }
Piano Tone Sample

The AudioBufferSourceNode has a 'loop' property, when set to true, the sample is replayed indefinitely. The node has two properties to influence the frequency of the sample; detune and playbackRate. Detune has influence on the playbackrate as well, but uses a value in cents. 100 cents is halve a tone distance; the difference in frequency between 2 consecutive keys on a keyboard. The playbackrate is 1 by default, the native sampling rate of the sound. When the playbackrate is doubled, then the playback frequency or tone also doubles in hight. The native tone of the piano tone sample is C4 or 261.63Hz. When it should sound at A4 or 440Hz, then use this formula: playbackRate = 440 / fSample = 440 / 261.63 = 1.68. The 'Grand Piano' demo uses the same sample foar all of it's tones, but the playbackRate for each tone differs.

Grand Piano

Sample library

When you want to use short audio samples in your project, eg a Midi player, you need an easy way to load and save the samples. Instead of having to load a lot of samples first at the start of your app, it's much easier to put all the samples in a library file, along with information about its type, load the single file and retrieve your samples.

I made a software module that takes care of this, you can download the .js file and link it to your project. I also made an app that makes it easy to compile a library file. You can find it here. There two sample libraries available for download. One with three samples of a Yamaha grand piano, and one with 13 samples of percussion instruments. I took the samples from this website and adjusted them a bit with Audacity.

Usage of the library software

The module has 4 functions:

  1. fileLib.newLib(header) Starts a new library. Header is a javascript Object. You can put all the info in the header you need for your project.
  2. fileLib.addFileData(header, fileData) Adds the data for a single sample to the library. Header is an Object containing information about the sample, filedata is an ArrayBuffer.
  3. fileLib.outBuffer() Returns an arraybuffer containing header and sample data. After this function is called, the internal bufferpointer is reset. Adding additional samples will overwrite the buffer contents.
  4. fileLib.readFile(fileBuffer) Reads a library file, returns an array. Each element contains an object with 2 members. 1: header, an object containing the header information. 2: data, an ArrayBuffer containing the sample data. Note that the first element of the array, index 0, contains the file header, the data member of the first element is null.

Instead of a javascript Object for header, you may also pass an ArrayBuffer for a binary header. When you do this then the returned header will allso be of type ArrayBuffer. Object headers are stored as UTF-8 JSON strings.


Make a new library, add a sample file, then save the library.

var fileheader = {name:'Midipercussion', date:'20180204'}; // put here anything you need fileLib.newLib(fileheader); var fileName = 'pianosample.ogg'; var sampleData = openFile(filename); // your file function returning an ArrayBuffer var sampleHeader= {title:'Yamaha-MU90R-ConcertGrand-C4', midiNr:42, note:'C', octave:4}; fileLib.addFileData(sampleHeader, sampleData); var buffer = fileLib.outBuffer(); // returns an ArrayBuffer var fileName = 'pianosamples'; saveFile(fileName, buffer) // your file writer function to save the file

Read samples back from the library

var data = openFile(fileName); // a function to get a file, must return an ArrayBuffer var Samples = fileLib.readFile(data); // get the array containing the samples processLibHeader(Samples[0].header); // Samples[0] is the fileheader for(var ct = 1; ct < Samples.length; ct++){ processSample(Samples[ct]); // Samples[1...n] are the sample data }

The processSample function decodes the raw sample data, and replaces it by an AudioBuffer.

©2018 vdVeen.net