Web Worker Metronome

https://gist.github.com/nick-thompson/4551106

by Patrick Hund

JavaScript

/*!
 * Metronome.js
 *
 * A reliable metronome using HTML5 Web Workers to maintain the tick
 * interval even when the parent thread is run in the background.
 *
 * For details, see:
 * http://pivotallabs.com/chrome-and-firefox-throttle-settimeout-setinterval-in-inactive-tabs/
 *
 * Copyright 2012 Nick Thompson
 * MIT License
 */

(function (window, undefined) {

  var msgHandler = function (e) {
        if (e.data.action === "start") {
          var freq = (60000 / e.data.bpm) / e.data.res;
          if (e.data.reduce) { freq = freq - (freq * 0.1); }
          timer = setInterval(function () {
          	self.postMessage({});
          }, freq);
        } else if (e.data.action === "stop") {
          clearInterval(timer);
        }
      }

    , inline = "" +
        "self.addEventListener('message', " + msgHandler.toString() + ");";

  /**
   * Metronome class constructor.
   *
   * @param {number} bpm Beats per minute
   * @param {number} res Resolution; metronome ticks per beat
   */
  function Metronome (bpm, res) {
    this.bpm = bpm;
    this.res = res;
    this.ticks = 0;

    var that = this
      , url = window.URL || window.webkitURL
      , blob = new Blob([inline])
      , blobUrl = url.createObjectURL(blob);

    this.worker = new Worker(blobUrl);

    this.worker.addEventListener("message", function (e) {
      that.ticks++;
      that.trigger("tick", that.ticks);
      that.trigger(that.ticks);
    });

  }

  /**
   * Add an event listener to the Metronome.
   *
   * On every tick of the metrome, listeners bound to the "tick"
   * event will be called with a single argument which is the number
   * of times the metronome has ticked so far.
   *
   * Additionally, on the nth tick of the metronome, listeners bound
   * to the event `n` (number) will be called.
   *
   * @param {string|number} e
   * @param {function} listener
   */
  Metronome.prototype.on = function (e, listener) {
    this._events = this._events || {};
    this._events[e] =...