Writing

HTML5 Audio: Easy and Not

Out of all the new features of HTML5, <audio> and <video> is probably the most written about. Though they are really easy to implement, I found them surprisingly difficult to work with once I started to scratch the surface.

One cool thing about it, is that the spec is written so that anything you put inside the element will be ignored by browsers that understand the tags. For browsers that don’t, the inner content will render. This gives these tags a powerful mechanism for use in progressive enhancement, and gives you no excuse for starting to use it right now. Except…

Hold Up a Minute, Why Two?

… There are two file types you have to upload in order to use these tags correctly (sigh). Seriously. The two largest file types any website will ever encounter (video and audio files, obviously) have to be duplicated in order to make it work across all HTML5 ready browsers. Webkit recoginzes .mp4 video and .mp3 audio. Mozilla, on the other hand is insisting on supporting .ogg for video and audio. So, that means for every piece of audio or video, you have to encode it twice, upload it twice, and code the source twice.

At first this was a dealbreaker for me. I felt that if a new technology requires something so drastic, it’s not ready yet. However, as time went on I felt like I needed to just get over it and do it, so I started converting the audio and video on my site to html5 audio and video. It was cool, and I really like using it. But it was only simple if you use the native browser controls.

It’s Not as Easy As I Thought

I decided to build an iTunes-like player in the site for my musical portfolio, where multiple audio files will be loaded dynamically into one audio element. Interacting with the audio API was where I started to stumble.

Problems With jQuery

I love jQuery, and I use it for everything, so much so, that I think of it as just being "Javascript". This gave me a few headaches though, when I tried to use the audio play API with jQuery objects.

Typically, I put all my selectors in the head of a script, assigning them variable names to be called later. That way, you’re only forcing the browser to traverse through the entire DOM once at the beginning, instead of every time you call an action. But I had some problems when I did this with the audio element:

  var audio = $('audio#player');
  audio.play();

Nothing happened. The command .play() is supposed to play the audio object, but it wouldn’t work, even though "audio.html()" would output [HTMLAudioObject]. Instead, I had to do this:

  var audio = getElementById('player');
  audio.play();

And then it worked. The audio and video API commands don’t seem to work yet with jQuery objects. They need a "vanilla" javascript object to work. Perhaps this is somethig that jQuery will need to implement in their source code in order to pass the right function to the object itself. Also, I imagine in the first example, "audio.html().play()" might have worked, but I didn’t try that.

However, once I discovered this, I had no problem integrating the "vanilla" JS object into the rest of my jQuery-heavy code.

Problems With Dynamic Source Code

The other major stumbling block I faced making this player was in using dynamic source code for the audio element to load.

In an iTunes-style player, there is one player and a list of songs. Each song that’s clicked needs to deliver the corresponding source code to the <source> tags inside the audio element.

The function I used looked something like this:

  var audio = getElementById('player');
  var mp3source = $(source#mp3);
  var oggsource = $(source#ogg);

  function playSong(obj){

      mp3source.attr('src', obj.mp3src);
      oggsource.attr('src', obj.oggsrc);

      audio.addEventListener("canplay", function(){
          audio.play();
      }, true);

      audio.load();
  }

And it worked like a charm in Chrome (my browser of choice). But when I tested it in Firefox, no luck. After much debugging, I discovered that everything was working, except that the new source data was not being loaded, even though the "src" attribute was changing in the element inspector.

The solution ended up being to remove and re-append the source elements before loading:

  var audio = getElementById('player');
  var mp3source = $(source#mp3);
  var oggsource = $(source#ogg);

  function playSong(obj){

      mp3source.attr('src', obj.mp3src).appendTo(audio);
      oggsource.attr('src', obj.oggsrc).appendTo(audio);

      audio.addEventListener("canplay", function(){
          audio.play();
      }, true);

      audio.load();
  }

Once I did that, it worked in Firefox ad Opera as well.

Edit: The version of Firefox I was using at the time was three point something. As of version four and later, this workaround isn’t necessary.

The Result…

The result is on the music section of my site (http://josephrogermoore.com/music). Be the judge for yourself to see if it’s worth starting to use now. I think it is, and the sooner talented developers start using it, the sooner it will become standardized and easier to do. So get to HTML5ing. It’s not that hard. Really.

2 Responses to “HTML5 Audio: Easy and Not”

  1. Chris Pearce says:

    “But when I tested it in Firefox, no luck. After much debugging, I discovered that everything was working, except that the new source data was not being loaded, even though the “src” attribute was changing in the element inspector.”

    I work on Firefox’s HTML5 audio implementation. At the time you were testing this, Firefox had implemented a version of the HTML5 specification which required media loads to only be started when load() was called, or when the media element was added to a document. The HTML5 specification has since changed, and Firefox 4+ has updated its load algorithm so that loads now start when the src attribute is changed.

  2. Joseph Moore says:

    Noted, and reflected in the post. Thanks for the info!

Leave a Reply