01 8 / 2011

Reading .mp3 ID3 tags in JavaScript

For a recent project, I needed to read an .mp3’s ID3 metadata (song title, artist, year, album) in pure JS. The idea was to have the user select a song file, and boom!, its info would display to the user. Good news…totally easy with the FileReader API and JS typed arrays.

Initially, I did a quick search to find some examples, but all of the examples I found used older techniques like manipulating a binary string. Ugh! We have better tools now for working with binary data in JS. Typed arrays to the rescue, specifically DataView.

DataView is a cousin to ArrayBufferView, which is a “view” of a portion of an ArrayBuffer. Array buffers represent chunks of bytes in memory. Multiple views can be created from a single ArrayBuffer. For example, one could create a Int8Array and a Float32Array from the same underlying data. Hence, “views”. This property makes them extremely versatile for binary data.

For my purposes, DataView turned out to be a perfect container for pulling sections of bytes as a string. However, I found the API to be a bit unintuitive in practice. Fortunately, I stumbled upon Christopher Chedeau's jDataView a while back and things started making sense. jDataView is an excellent wrapper for DataView, improving much of its jankiness and adding a few extra utility methods for things like seeking and getting at data (e.g. getString(), getChar()).

Here’s all the code that I needed:

document.querySelector('input[type="file"]').onchange = function(e) {
  var reader = new FileReader();

  reader.onload = function(e) {
    var dv = new jDataView(this.result);

    // "TAG" starts at byte -128 from EOF.
    // See http://en.wikipedia.org/wiki/ID3
    if (dv.getString(3, dv.byteLength - 128) == 'TAG') {
      var title = dv.getString(30, dv.tell());
      var artist = dv.getString(30, dv.tell());
      var album = dv.getString(30, dv.tell());
      var year = dv.getString(4, dv.tell());
    } else {
      // no ID3v1 data found.
    }
  };

  reader.readAsArrayBuffer(this.files[0]);
};

Pretty slick.

DataView is implemented in Chrome 9+ and Webkit nightlies. However, jDataView provides the DataView API for all the browsers using the best available option between Strings, JS typed arrays, and DataView.