The Sound of JavaScript


Barak Chamo

@GoFoolhardy

Content Warning

🔊 loud music 🔊

💫 fast animations 💫

Let's make some noise
in the browser!

WebAudio + Web MIDI + Visualization =

🎉💥😱💥😱💥😱💥🎉

🎶 WebAudio 🎶

WebAudio is an interface for controlling audio on the web.

Operating in an Audio Context, we perform audio operations using Audio Nodes and link them together to create a Modular Routing Graph.

🎶 Synthesis 🎶

Oscillators

// Get audio context
const context = new AudioContext()

// Create 2 nodes, an oscillator and a gain
const gain = context.createGain()
const osc = context.createOscillator()

// Configure the oscillator to play a sawtooth waveshape
osc.type = {WAVESHAPE}

// Connect the oscillator and gain to the audio output
osc.connect(gain)
gain.connect(context.destination)

// Start the oscillator
osc.start(0)
/* Create 3 oscillators, for playing a triad */
const osc1 = context.createOscillator()
const osc2 = context.createOscillator()
const osc3 = context.createOscillator()

/* Set the base frequency and the ratios for a fifth and octave up */
osc1.frequency.value = {FREQ}
osc2.frequency.value = {FREQ} * 1.5
osc3.frequency.value = {FREQ} * 2

/* Start the oscillators, playing the chord */
osc1.start(0)
osc2.start(0)
osc3.start(0)

Signal Processing

// Create a filter node
var filter = context.createBiquadFilter()

// Set filter type
filter.type = 'lowpass'

// Set filter cutoff frequency
filter.frequency.value = 20000

// Connect oscillator through filter
osc.connect(filter)


Filter Cutoff

Modulation

... Modular Routing Graph.

// Create control wave oscillator node
var lfoGain = AUDIO.createGain()
var lfo = AUDIO.createOscillator()

// Create a filter node
var filter = AUDIO.createBiquadFilter()

// Set the LFO range with the LFO gain
lfo.connect(lfoGain)

// Control filter's frequency  with the LFO
lfoGain.connect(filter.frequency)

// Start the LFO
lfo.start(0)


LFO Rate

Envelope



Envelope

Attack




Release




Timing / Composition

🔊 Sampling 🔊

/* Decode arraybuffer sample */
audioContext.decodeAudioData(arrayBuffer, audioBuffer => {

  /* Create an audio buffer source */
  var source = AUDIO.createBufferSource()

  /* assign buffer to source */
  source.buffer = audioBuffer

  /* connect to output */
  source.connect(OUTPUT)

  /* Start sample playback */
  source.start(0)

})

function playSample(sample, time) {
  /* Create a new audio source */
  var source = AUDIO.createBufferSource()
  source.buffer = sample

  /* Connect source to output */
  source.connect(OUTPUT)

  /* Start the sample in a time in the future */
  source.start(time || 0)
}

🎹 Web MIDI 🎹

Musical Instrument Digital Interface

MIDI Access

var midi = navigator.requestMIDIAccess().then(onMidiAccess)

function onMidiAccess(midiAccess){
  var inputs = midi.inputs.values()

  // loop over all available inputs and listen for any MIDI input
  for (let input of inputs) {
    input.onmidimessage = onMIDIMessage
  }
}

MIDI Messages

function onMIDIMessage(message){
  var data = message.data; // [command/channel, note, velocity]

  // Check for noteOn commands
  if(data[0] === 144) {
    // Play the note
    playNote(data[1])
  }
}
function midiNoteToFrequency(midiNote){
  return Math.pow(2, (note - 69) / 12) * 440
}

MIDI CC

continuous controller

Web MIDI Theremin

📈 WebAudio Analyser 📉

The analyser node provides real-time frequency and time-domain analysis information.

Time-Domain

Frequency

Beat Detection

DETECT BEATS


Pitch Detection

--

--

Recurse Center

recurse.com

Thanks!


Barak Chamo

@GoFoolhardy