├── .DS_Store
├── .gitignore
├── 1-Oscillators-and-Frequency
├── 1.js
├── README.md
└── index.html
├── 2-Using-Math-to-Make-Notes
├── 2.js
├── README.md
└── index.html
├── 3-Scheduling-Things-in-Advance
├── 3.js
├── README.md
└── index.html
├── 4-Continuous-Sequencing
├── 4.js
├── README.md
└── index.html
├── 5-Playing-Samples
├── 5.js
├── README.md
├── index.html
└── synth.mp3
├── 6-Manipulating-Samples
├── 6.js
├── README.md
├── drums.mp3
└── index.html
├── README.md
├── css
├── boxes.less
├── buttons.less
├── jquery-ui.min.less
├── sequencer.less
└── site.less
├── dependencies
├── jquery-ui.min.js
├── jquery.min.js
└── less.min.js
└── index.html
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kylestetz/Web-Audio-Basics/6fe8ff88db482419f7a4df461ef433a77e52f199/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
--------------------------------------------------------------------------------
/1-Oscillators-and-Frequency/1.js:
--------------------------------------------------------------------------------
1 | $( function() {
2 |
3 | // Set up our Audio Context
4 | var context = new AudioContext();
5 | window.context = context;
6 |
7 | // this is where our oscillator will live
8 | var oscillator;
9 |
10 | // grab our button elements so we can attach events to them
11 | var noteOn = $('#note-on');
12 | var noteOff = $('#note-off');
13 | // get the x and y span elements so we can display values in them
14 | var x = $('#x');
15 | var y = $('#y');
16 |
17 | noteOn.on('click', function(e) {
18 | // if the oscillator has a value, let's assume that means
19 | // it's playing and go away. If we don't do this, we'll create
20 | // a new reference and the old one will hang around forever,
21 | // inaccessible to us.
22 | if (oscillator) {
23 | return;
24 | }
25 | // create the oscillator node
26 | oscillator = context.createOscillator();
27 | // set its type
28 | oscillator.type = 'sine'; // sine, triangle, sawtooth, square
29 | // set its frequency in Hertz
30 | // oscillator.frequency.value = 334;
31 | // connect it to the output
32 | oscillator.connect(context.destination);
33 | // play it!
34 | // a value other than 0 will allow us to schedule it in the future
35 | oscillator.start(0);
36 | });
37 |
38 | noteOff.on('click', function(e) {
39 | if(oscillator) {
40 | // stop the oscillator immediately
41 | oscillator.stop(0);
42 | // set the variable to null so that we know nothing is playing.
43 | oscillator = null;
44 | }
45 | });
46 |
47 | $('body').on('mousemove', function(e) {
48 | // we could do some math to put the values in useful number ranges...!
49 | var xValue = e.clientX;
50 | var yValue = e.clientY;
51 |
52 | // take a look at the values of the text
53 | x.text(xValue);
54 | y.text(yValue);
55 |
56 | // if we don't have an oscillator running right now then we have
57 | // nothing else to do here and should return.
58 | if (!oscillator) {
59 | return;
60 | }
61 |
62 | // set the frequency to the x position of the mouse!
63 | oscillator.frequency.value = xValue;
64 | });
65 |
66 | });
--------------------------------------------------------------------------------
/1-Oscillators-and-Frequency/README.md:
--------------------------------------------------------------------------------
1 | # 1 - Oscillators and Frequency
2 |
3 | In this sketch we create a sine wave and control its pitch with the X axis of the mouse. It sounds a bit like a theremin.
4 |
5 | Codepen:
6 | http://codepen.io/kylestetz/pen/waxQQb
--------------------------------------------------------------------------------
/1-Oscillators-and-Frequency/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Web Audio Basics - Oscillators and Frequency
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Oscillators and Frequency
12 |
13 |
14 |
15 |
16 |
17 | (0, 0)
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/2-Using-Math-to-Make-Notes/2.js:
--------------------------------------------------------------------------------
1 | $( function() {
2 |
3 | // Set up our Audio Context
4 | var context = new AudioContext();
5 | window.context = context;
6 |
7 | // this is where our oscillator will live
8 | var oscillator;
9 |
10 | // numbers we'll need later
11 | var width = $(window).width();
12 | var height = $(window).height();
13 | var xValue = 0;
14 | var yValue = 0;
15 |
16 | // recalculate the width and height if the window size changes
17 | $(window).resize( function() {
18 | width = $(this).width();
19 | height = $(this).height();
20 | });
21 |
22 | $('body').on('mousedown', function(e) {
23 | if (oscillator) {
24 | oscillator.stop(0);
25 | }
26 | // create the oscillator
27 | oscillator = context.createOscillator();
28 | // set the type of the oscillator
29 | oscillator.type = 'triangle'; // sine, triangle, sawtooth
30 | // set the frequency based on our stored values
31 | oscillator.frequency.value = mtof(xValue + yValue);
32 | // connect it to the output
33 | oscillator.connect(context.destination);
34 | // start the note
35 | oscillator.start(0);
36 | });
37 |
38 | $('body').on('mouseup', function(e) {
39 | // stop the note
40 | oscillator.stop(0);
41 | });
42 |
43 | $('body').on('mousemove', function(e) {
44 | // do some math to put the values in useful number ranges:
45 | // convert (0 <-> window width) to (0 <-> 13) and chop off the decimal
46 | xValue = 45 + Math.floor( e.clientX / width * 13 );
47 | // convert (0 <-> window height) to (0 <-> 4) and chop off the decimal
48 | yValue = Math.floor( e.clientY / height * 4 ) * 12;
49 |
50 | if (!oscillator) {
51 | return;
52 | }
53 |
54 | // the "de-zippered" (smoothed) version
55 | // oscillator.frequency.value = mtof(xValue + yValue);
56 |
57 | // the "zippered" (unsmoothed) version
58 | oscillator.frequency.setValueAtTime( mtof(xValue + yValue), context.currentTime );
59 | });
60 |
61 | });
62 |
63 | // mtof = Midi note to Frequency
64 | // input: 0 - 127 (although you could go higher if you wanted)
65 | // output: frequency in Hz from ~8Hz to ~12543Hz
66 | function mtof(note) {
67 | return ( Math.pow(2, ( note-69 ) / 12) ) * 440.0;
68 | }
--------------------------------------------------------------------------------
/2-Using-Math-to-Make-Notes/README.md:
--------------------------------------------------------------------------------
1 | # 2 - Using Math to Make Notes
2 |
3 | In this sketch we do some math with the X and Y coordinates of the mouse in order to create a grid of notes. Some HTML and CSS provide a visual representation of what's going on.
4 |
5 | This sketch introduces the `mtof` function, which allows us to work with MIDI notation (notes in the range 0 - 127) and convert that to frequency. You can read more about MIDI notation and the formula for conversion here: [_Note names, MIDI numbers and frequencies_](http://newt.phys.unsw.edu.au/jw/notes.html).
6 |
7 | Codepen:
8 | http://codepen.io/kylestetz/pen/pJZQqj
--------------------------------------------------------------------------------
/2-Using-Math-to-Make-Notes/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Web Audio Basics - Using Math to Make Notes
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/3-Scheduling-Things-in-Advance/3.js:
--------------------------------------------------------------------------------
1 | $( function() {
2 |
3 | // Set up our Audio Context
4 | var context = new AudioContext();
5 | window.context = context;
6 |
7 | var melody = [69, 71, 73, 74, 76, 78, 80, 81];
8 |
9 | var noteLength = 1/4;
10 | var attack = 1/64;
11 |
12 | function scheduleNote(note, time) {
13 | var oscillator = context.createOscillator();
14 | // create an envelope using gain
15 | var gain = context.createGain();
16 |
17 | oscillator.frequency.value = mtof(note);
18 |
19 | // connect the oscillator to the gain and the gain to the output
20 | oscillator.connect(gain);
21 | gain.connect(context.destination);
22 |
23 | // let's make an envelope with almost no attack and a sharp decay...
24 | // starting value of 0:
25 | gain.gain.setValueAtTime(0, time);
26 | // very quick attack to a value of 1:
27 | gain.gain.linearRampToValueAtTime(1, time + attack);
28 | // immediate decay to a value of 0:
29 | gain.gain.linearRampToValueAtTime(0, time + noteLength);
30 |
31 | // schedule the oscillator to start at `time` and end
32 | // at `time + noteLength`
33 | oscillator.start(time);
34 | oscillator.stop(time + noteLength);
35 | }
36 |
37 | $('.play').on('click', function(e) {
38 | // when the play button is clicked, schedule the melody
39 | for(var i = 0; i < melody.length; i++) {
40 | scheduleNote( melody[i], context.currentTime + (1/4 * i) );
41 | }
42 | });
43 |
44 | });
45 |
46 | // mtof = Midi note to Frequency
47 | // input: 0 - 127 (although you could go higher if you wanted)
48 | // output: frequency in Hz from ~8Hz to ~12543Hz
49 | function mtof(note) {
50 | return ( Math.pow(2, ( note-69 ) / 12) ) * 440.0;
51 | }
--------------------------------------------------------------------------------
/3-Scheduling-Things-in-Advance/README.md:
--------------------------------------------------------------------------------
1 | # 3 - Scheduling Things in Advance
2 |
3 | If we want accurate timing we have to schedule our sounds in advance. In this sketch we schedule a melody to be played every time the `play` button is pressed.
4 |
5 | Codepen:
6 | http://codepen.io/kylestetz/pen/aOjQPw
--------------------------------------------------------------------------------
/3-Scheduling-Things-in-Advance/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Web Audio Basics - Scheduling Things in Advance
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Scheduling Things in Advance
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/4-Continuous-Sequencing/4.js:
--------------------------------------------------------------------------------
1 | $( function() {
2 |
3 | // Set up our Audio Context
4 | var context = new AudioContext();
5 | window.context = context;
6 |
7 | // Use this array to store the values of all the sliders.
8 | var values = [69, 69, 69, 69, 69, 69, 69, 69];
9 |
10 | // grab the sliders from the DOM and bind a `change` event.
11 | var $sliders = $('.slider');
12 | $sliders.on('change', function(e) {
13 | // if the slider changes, store its new value in the appropriate
14 | // index of the `values` array.
15 | values[ $(this).index() ] = $(this).val();
16 | });
17 |
18 | // the checkbox will control the start & stop of the sequencer.
19 | $('.checkbox').on('change', function(e) {
20 | if( this.checked ) {
21 | // start the sequencer
22 | nextNoteTime = context.currentTime;
23 | scheduleSequence();
24 | intervalId = setInterval(scheduleSequence, intervalTime);
25 | } else {
26 | // stop the sequencer
27 | intervalId = clearInterval(intervalId);
28 | }
29 | });
30 |
31 | // ================================================================
32 | // TIMING
33 | // ================================================================
34 |
35 | var bpm = 120;
36 | // an eighth note at the given bpm
37 | var noteLength = bpm / 60 * (1/8);
38 | var attack = 1/64;
39 |
40 | var lookahead = 0.04; // 40ms expressed in seconds
41 | var intervalTime = 25; // 25ms expressed in milliseconds
42 |
43 | // these will keep track of the state of the sequencer:
44 | // when the next note is happening
45 | var nextNoteTime = null;
46 | // the index of the current note from 0 - 7
47 | var currentNote = 0;
48 | // the id of the setInterval lookahead
49 | var intervalId = null;
50 |
51 | function scheduleSequence() {
52 | while(nextNoteTime < context.currentTime + lookahead) {
53 | // schedule the next note
54 | scheduleNote( values[currentNote], nextNoteTime, currentNote );
55 | // advance the time
56 | nextNoteTime += noteLength;
57 | // keep track of which note we're on
58 | currentNote = ++currentNote % values.length;
59 | }
60 | }
61 |
62 | function scheduleNote(note, time, current) {
63 | var oscillator = context.createOscillator();
64 | // create an envelope using gain
65 | var gain = context.createGain();
66 |
67 | oscillator.frequency.value = mtof(note);
68 |
69 | // connect the oscillator to the gain and the gain to the output
70 | oscillator.connect(gain);
71 | gain.connect(context.destination);
72 |
73 | // let's make an envelope with almost no attack and a sharp decay...
74 | // starting value of 0:
75 | gain.gain.setValueAtTime(0, time);
76 | // very quick attack to a value of 1:
77 | gain.gain.linearRampToValueAtTime(1, time + attack);
78 | // immediate decay to a value of 0:
79 | gain.gain.linearRampToValueAtTime(0, time + noteLength);
80 |
81 | // schedule the oscillator to start at `time` and end
82 | // at `time + noteLength`
83 | oscillator.start(time);
84 | oscillator.stop(time + noteLength);
85 |
86 | // add the time and slider index to our animation queue so that
87 | // we can highlight the correct slider when it's time.
88 | animationQueue.push({ time: time, slider: current });
89 | }
90 |
91 | // ================================================================
92 | // VISUAL
93 | // ================================================================
94 |
95 | // The animation queue is an array with scheduled notes in it.
96 | // Within `requestFrameAnimation` we will check the next `time` in
97 | // the queue against `context.currentTime` to determine when we
98 | // should highlight a slider or not.
99 |
100 | var animationQueue = [];
101 |
102 | function highlightCurrentSlider() {
103 | while(animationQueue.length && context.currentTime > animationQueue[0].time) {
104 | // turn off all `highlight` classes
105 | $sliders.toggleClass('highlight', false);
106 | // highlight the current slider
107 | $sliders.eq(animationQueue[0].slider).addClass('highlight');
108 | // remove this item from the queue
109 | animationQueue.splice(0, 1);
110 | }
111 | // request the next frame
112 | requestAnimationFrame(highlightCurrentSlider);
113 | }
114 |
115 | highlightCurrentSlider();
116 |
117 | });
118 |
119 | // mtof = Midi note to Frequency
120 | // input: 0 - 127 (although you could go higher if you wanted)
121 | // output: frequency in Hz from ~8Hz to ~12543Hz
122 | function mtof(note) {
123 | return ( Math.pow(2, ( note-69 ) / 12) ) * 440.0;
124 | }
--------------------------------------------------------------------------------
/4-Continuous-Sequencing/README.md:
--------------------------------------------------------------------------------
1 | # 4 - Continuous Scheduling
2 |
3 | In this sketch we tackle the complexity of scheduling in advance while still allowing the user control over the sound. The technique used is based on the article [_A Tale of Two Clocks_](http://www.html5rocks.com/en/tutorials/audio/scheduling/) by Chris Wilson.
4 |
5 | Codepen:
6 | http://codepen.io/kylestetz/pen/oXMQJE
--------------------------------------------------------------------------------
/4-Continuous-Sequencing/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Web Audio Basics - Continuous Sequencing
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/5-Playing-Samples/5.js:
--------------------------------------------------------------------------------
1 | $( function() {
2 |
3 | // Set up our Audio Context
4 | var context = new AudioContext();
5 | window.context = context;
6 |
7 | // here's where we'll store our sample once it's loaded.
8 | var sampleBuffer = null;
9 |
10 | loadSample('synth.mp3', function(sample) {
11 | sampleBuffer = sample;
12 | });
13 |
14 | $('#play-sample').on('click', function(e) {
15 | // don't play the sample unless it has loaded!
16 | if(sampleBuffer) {
17 | // create an AudioBufferSource, which can hold and play a sample.
18 | var source = context.createBufferSource();
19 | // set its buffer to our sampleBuffer
20 | source.buffer = sampleBuffer;
21 | // now we can treat it like any audio node
22 | source.connect(context.destination);
23 | source.start(0);
24 | }
25 | });
26 |
27 | });
28 |
29 | // Here's an abstraction of the sample loading process. Since this
30 | // involves an asynchronous request, we give it a callback to signal
31 | // its completion. The callback gets the decoded sample buffer.
32 |
33 | function loadSample(path, callback) {
34 | // create a new http request
35 | var request = new XMLHttpRequest();
36 | // it's a GET request for the url `synth.mp3`, which in a more
37 | // typical setup might be `/sounds/synth.mp3` or some other static
38 | // file route.
39 | request.open('GET', path, true);
40 | // we want the browser to interpret the data as an arraybuffer,
41 | // which is the format the web audio API expects samples in.
42 | request.responseType = 'arraybuffer';
43 | // tell the request what to do when it's done
44 | request.onload = function() {
45 | // this function turns our arraybuffer into an AudioBuffer
46 | // that the web audio api can understand.
47 | context.decodeAudioData(request.response, callback);
48 | };
49 | // send the request
50 | request.send();
51 | }
--------------------------------------------------------------------------------
/5-Playing-Samples/README.md:
--------------------------------------------------------------------------------
1 | # 5 - Playing Samples
2 |
3 | In this sketch we play a sample, no frills. The code for loading them has been adapted from the [Getting Started with Web Audio](http://www.html5rocks.com/en/tutorials/webaudio/intro/) article by Boris Smus. Worth a read!
4 |
5 | Codepen:
6 | http://codepen.io/kylestetz/pen/jPQEPo
--------------------------------------------------------------------------------
/5-Playing-Samples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Web Audio Basics - Oscillators and Frequency
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Playing A Sample
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/5-Playing-Samples/synth.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kylestetz/Web-Audio-Basics/6fe8ff88db482419f7a4df461ef433a77e52f199/5-Playing-Samples/synth.mp3
--------------------------------------------------------------------------------
/6-Manipulating-Samples/6.js:
--------------------------------------------------------------------------------
1 | $( function() {
2 |
3 | // Set up our Audio Context
4 | var context = new AudioContext();
5 | window.context = context;
6 |
7 | // here's where we'll store our sample once it's loaded.
8 | var sampleBuffer = null;
9 | var sampleNode = null;
10 | // how fast is the sample playing?
11 | var playbackRate = 1;
12 | // what part of the sample are we playing?
13 | var loopStart = 0;
14 | var loopEnd = 1;
15 |
16 | // load the sample
17 | loadSample('drums.mp3', function(sample) {
18 | sampleBuffer = sample;
19 | });
20 |
21 | // a jquery ui component for controlling the playback speed
22 | $('#speed-range').slider({
23 | min: 0.1,
24 | max: 4,
25 | step: 0.01,
26 | value: 1,
27 | // this callback is fired every time the slider is moved
28 | slide: function(e, ui) {
29 | playbackRate = ui.value;
30 | if(sampleNode) {
31 | sampleNode.playbackRate.value = playbackRate;
32 | }
33 | }
34 | });
35 |
36 | // a jquery ui component for controlling the sample range
37 | $('#sample-range').slider({
38 | range: true,
39 | min: 0,
40 | max: 1,
41 | step: 0.01,
42 | values: [0, 1],
43 | // this callback is fired every time either slider is moved
44 | slide: function(e, ui) {
45 | loopStart = ui.values[0];
46 | loopEnd = ui.values[1];
47 | if(sampleNode) {
48 | sampleNode.loopStart = loopStart;
49 | sampleNode.loopEnd = loopEnd;
50 | }
51 | }
52 | });
53 |
54 | $('#start-sample').on('click', function(e) {
55 | // don't play the sample unless it has loaded and it's not already playing
56 | if(sampleBuffer) {
57 | if(sampleNode) {
58 | sampleNode.stop(0);
59 | }
60 | // create an AudioBufferSource, which can hold and play a sample.
61 | sampleNode = context.createBufferSource();
62 | // set its buffer to our sampleBuffer
63 | sampleNode.buffer = sampleBuffer;
64 | // apply our loop, playbackRate, and sample loop points
65 | sampleNode.loop = true;
66 | sampleNode.playbackRate.value = playbackRate;
67 | sampleNode.loopStart = loopStart;
68 | sampleNode.loopEnd = loopEnd;
69 | // now we can treat it like any audio node
70 | sampleNode.connect(context.destination);
71 | sampleNode.start(0);
72 | }
73 | });
74 |
75 | $('#stop-sample').on('click', function(e) {
76 | if(sampleBuffer && sampleNode) {
77 | sampleNode.stop(0);
78 | sampleNode = null;
79 | }
80 | });
81 |
82 | });
83 |
84 | // Here's an abstraction of the sample loading process. Since this
85 | // involves an asynchronous request, we give it a callback to signal
86 | // its completion. The callback gets the decoded sample buffer.
87 |
88 | function loadSample(path, callback) {
89 | // create a new http request
90 | var request = new XMLHttpRequest();
91 | // it's a GET request for the url `synth.mp3`, which in a more
92 | // typical setup might be `/sounds/synth.mp3` or some other static
93 | // file route.
94 | request.open('GET', path, true);
95 | // we want the browser to interpret the data as an arraybuffer,
96 | // which is the format the web audio API expects samples in.
97 | request.responseType = 'arraybuffer';
98 | // tell the request what to do when it's done
99 | request.onload = function() {
100 | // this function turns our arraybuffer into an AudioBuffer
101 | // that the web audio api can understand.
102 | context.decodeAudioData(request.response, callback);
103 | };
104 | // send the request
105 | request.send();
106 | }
--------------------------------------------------------------------------------
/6-Manipulating-Samples/README.md:
--------------------------------------------------------------------------------
1 | # 6 - Manipulating Samples
2 |
3 | In this sketch we loop a drum sample and control its playback speed and loop points. jQuery UI is used for the sliders.
4 |
5 | Codepen:
6 | http://codepen.io/kylestetz/pen/vOvpOm
--------------------------------------------------------------------------------
/6-Manipulating-Samples/drums.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kylestetz/Web-Audio-Basics/6fe8ff88db482419f7a4df461ef433a77e52f199/6-Manipulating-Samples/drums.mp3
--------------------------------------------------------------------------------
/6-Manipulating-Samples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Web Audio Basics - Oscillators and Frequency
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |