Output ChannelOutput Channel

TR-808 Cowbell Recreation with Web Audio

September 29, 2015

I've had the idea for this blog post for a number of weeks - ever since reading Chris Lowis' article on creating drum sounds. Then, reading Joe Sullivan's article on recreating the TR-808 hi-hat motivated me to finally get this published.

The TR-808 cowbell has a strange allure over me. It sounds virtually nothing like an acoustic cowbell, but I like it because it's such a unique, interesting sound and is instantly recognisable. It's been used in tracks as varied as Whitney Houston's I wanna Dance with Somebody, Afrika Bambaataas Planet Rock and the xx's Together. Whenever I come across a new example, I've been adding the song to the ever expanding electronic cowbell playlist.

The original schematic diagrams for the 808 show that the cowbell is a relatively simple sound to produce. All you need is two square waves at specific frequencies of 800 and 540Hz. You pass these through an exponentially decaying amplitude envelope (a fancy way of saying you need to make the sound start loud, then quickly decay to nothing). Then finally, just pass the signal through a bandpass filter which takes away some of the low and high frequencies.

When doing anything in Web Audio, everything takes place in an AudioContext. This object holds all the important methods for creating oscillators, filters and all other nodes. Let's create one now:

  var context = new AudioContext();

Now, basing our code on the structure Chris used in his post, we can kick things off by creating a cowbell object. Initially it has just two properties, the audio context, and the duration (which we'll set to 0.5 seconds).

function Cowbell(context) {
  this.context = context;
  this.duration = 0.5;
};

Now let's create a setup method that creates the two oscillators, the filter and the gain node that allows us to fade the sound out. The two oscillators connect to the gain node, the gain node connects to the filter, and then the filter connects to our output device, context.destination.

Cowbell.prototype.setup = function() {
  this.osc1 = this.context.createOscillator();
  this.osc2 = this.context.createOscillator();
  this.osc1.type = "square";
  this.osc2.type = "square";
  this.osc1.frequency.value = 800;
  this.osc2.frequency.value = 540;
  this.gainNode = this.context.createGain();
  this.filter = this.context.createBiquadFilter();
  this.filter.type = "bandpass";
  this.osc1.connect(this.gainNode);
  this.osc2.connect(this.gainNode);
  this.gainNode.connect(this.filter)
  this.filter.connect(this.context.destination)
};

Now we'll create a method called trigger which we'll run when we want to play the cowbell sound. Because oscillator nodes can only be used once, trigger will include a call to setup, to regenerate the oscillators each time they are needed. After setup has been called, we make sure the gain node is at its maximum value of 1. We use exponentialRampToValueAtTime to gradually reduce the amplitude to a minimum value of 0.01. The last thing we need to do in this method is start both oscillators, and schedule them to stop after 0.5s.

Cowbell.prototype.trigger = function(time) {
  this.setup();
  this.gainNode.gain.setValueAtTime(0.5, time);
  this.gainNode.gain.exponentialRampToValueAtTime(0.01, time + this.duration);

  this.osc1.start(time);
  this.osc2.start(time);

  this.osc1.stop(time + this.duration);
  this.osc2.stop(time + this.duration);
};

That completes everything we need for the Cowbell object. All we need to do now is create an instance of the Cowbell, and attach an event listener to a button which will act as a basic user interface to trigger the sound.

<button id='js-more-cowbell'>More Cowbell</button>

<script>
  var context = new AudioContext();
  var cowbell = new Cowbell(context);
  $("#js-more-cowbell").click(function(e){
    e.preventDefault();
    cowbell.trigger(context.currentTime);
  });
</script>

Here's the finished example, in a Codepen embed. Please feel free to fork, share and tweak.

See the Pen Web Audio electronic cowbell by Ed Ball (@edball) on CodePen.