Skip to main content

Stylophone – Web Audio API

Stylophone emulation. Sounds created in-browser via the new Web Audio API.

Mike Foskett - (update: )

Stylophone: try before you buy

Experimenting with the Web Audio API. The Stylophone provides a nice and easy to follow example.

The sound is generated in browser, no samples, no MP3, no libraries, no huge code base. Just a simple SVG with a dash of JavaScript. Completely responsive too.

Sorry, but there's no keyboard, or touch device, support at present. Maybe in future…

Keyboard SVG

The keyboard is an SVG overlaid on the graphic. The rect make up the major keys while polyline are used for the sharps and flats.

The frequency of each note is stored as a data attribute attached to each key. The frequency values were taken from a Reverse Engineered Stylophone readings.

Language HTML
<svg id=keys class=keyboard width=590 viewbox="0 0 590 89">
  <rect data-freq="110.8" x="2"   width="47" height="89"/>
  <rect data-freq="124.4" x="51"  width="47" height="89"/>
  <rect data-freq="131.8" x="100" width="47" height="89"/>
  <rect data-freq="147.7" x="149" width="47" height="89"/>
  <rect data-freq="165.6" x="198" width="47" height="89"/>
  <rect data-freq="175.4" x="247" width="47" height="89"/>
  <rect data-freq="196.6" x="296" width="47" height="89"/>
  <rect data-freq="220.4" x="345" width="47" height="89"/>
  <rect data-freq="247.1" x="394" width="47" height="89"/>
  <rect data-freq="261.7" x="443" width="47" height="89"/>
  <rect data-freq="293.4" x="492" width="47" height="89"/>
  <rect data-freq="328.4" x="541" width="47" height="89"/>
  <polyline data-freq="117.5" points="24.5,0  71.5,0  71.5,42  24.5,42  24.5,0"/>
  <polyline data-freq="139.6" points="123.5,0  170.5,0  170.5,42  123.5,42  123.5,0"/>
  <polyline data-freq="156.4" points="172.5,0  219.5,0  219.5,42  172.5,42  172.5,0"/>
  <polyline data-freq="185.7" points="271.5,0  318.5,0  318.5,42  271.5,42  271.5,0"/>
  <polyline data-freq="208.2" points="320.5,0  367.5,0  367.5,42  320.5,42  320.5,0"/>
  <polyline data-freq="233.4" points="369.5,0  416.5,0  416.5,42  369.5,42  369.5,0"/>
  <polyline data-freq="277.0" points="467.5,0  514.5,0  514.5,42  467.5,42  467.5,0"/>
  <polyline data-freq="310.5" points="516.5,0  563.5,0  563.5,42  516.5,42  516.5,0"/>
</svg>

JavaScript - Web Audio API

As yet unpolished, almost raw. Couldn't be much simpler.

Language JavaScript
"use strict";

try {
  window.AudioContext = window.AudioContext || window.webkitAudioContext;
  window.audioContext = new window.AudioContext();
} catch (e) {
  console.log("No Web Audio API support");
}

// Create Web Audio Context.
var context = new window.AudioContext(),
    currentOscillator,
    noteOn = false;

The play and stop functions were robbed wholesale from Web Audio Stylophone. Though they were amended to work with modern syntax and the reliance on the Raphael vector library was completely removed.

Language JavaScript
function playFreq (frequency) {

  // Create oscillator and gain node.
  var oscillator = context.createOscillator(),
      gainNode = context.createGain();

  // Disconnect existing oscillator if there is one.
  if (currentOscillator) {
    currentOscillator.disconnect();
  }

  // Set the type and frequency of the oscillator.
  oscillator.type = "sawtooth";
  oscillator.frequency.value = frequency;

  // Set volume of the oscillator.
  gainNode.gain.value = 0.1;

  // Route oscillator through gain node to speakers.
  oscillator.connect(gainNode);
  gainNode.connect(context.destination);

  // Set the current oscillator to the one we've just created.
  currentOscillator = oscillator;

  // Start oscillator playing.
  oscillator.start(0);
};

var stopNote = function () {

  // Stop the current Oscillator from playing then disconnect it.
  if (currentOscillator) {
    currentOscillator.stop(0);
    currentOscillator.disconnect();
  }
};

Event delegation to cover all the SVG keys.

Language JavaScript
function listenKeys(e) {
  if (e.target && (e.target.nodeName === "rect" || e.target.nodeName === "polyline")) {

    switch (e.type) {
      case "mousedown" :
        noteOn = true;
        playFreq(e.target.getAttribute("data-freq"));
        break;
      case "mouseover" :
        if (noteOn) {
          playFreq(e.target.getAttribute("data-freq"));
        }
        break;
      case "mouseup":
        noteOn = false;
        stopNote();
        break;
    }
  }
}

var keys = document.getElementById("keys");
if (keys) {
  keys.addEventListener("mousedown", listenKeys, false);
  keys.addEventListener("mouseover", listenKeys, false);
  keys.addEventListener("mouseup", listenKeys, false);
}

To do

  • Add keyboard support, not accessible without.
  • Add touch support, just to be nice.
  • Maybe change the cursor to a stylus? A bit cumbersome though.
  • A detune rotary wouldn't hurt either.
  • Add vibrato switch to finish the emulation.
  • Explore custom oscillator waveforms for a more accurate sound.
  • Possibly extend further with chorus / echo / reverb and volume control?

Socialise: