Build a Custom HTML5 Audio Player with a File Selector

By Rob Gravelle

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

The HTML5 Video Player with Custom Controls Codepen demo that accompanied the Create Custom Controls for your HTML5 Video Player tutorial received a lot of love from Codepen users, promoting it to the number two spot of all my Codepen projects, second only to the Drag Files Into the Browser From the Desktop using jQuery Demo. As a follow-up, I'm going to give the HTML5 Audio player the same treatment so that we may give it a consistent — and may I say, more appealing — look across browsers. Today, we'll attach a file picker that loads the song from the user's device, and next time we'll code handlers for all of the controls.

Inspiration Behind the File Picker

While creating the demo, I came to the realization that I'd have to host some audio files on my Web server. Depending on how many people try the player out, that could place a sizable load on my server! That prompted me to provide a means for users to select a file from their own device via file input control:

<input type="file" accept="audio/*"/>
<div id="message"></div>
<audio id="music" autoplay="autoplay">
  Your browser does not support the audio format.
</audio> 

At first, I wasn't sure if you could just take the selected file and play it. Turns out that you cannot. But, there is a way using the static URL.createObjectURL() method. Part of the URL-interface, it constructs a URL based on the file. Specifically, it creates a reference to a File (or a Blob).

                       // id for audio element
var player = document.getElementById('music'); 

function playSelectedFile(event) {
    var file = this.files[0];
    player.src = URL.createObjectURL(file);
}

var inputNode = document.querySelector('input');
    inputNode.addEventListener('change', playSelectedFile, false);

Testing the File for Audio Content

If we're going to let the user choose a file, we had better make sure that it contains audio and not a spreadsheet. The AUDIO element comes with a handy function called canPlayType() that can be utilized to determine whether or not the selected file contains audio data. To do that, you have to pass it the file type. It should contain a value such as:

  • video/ogg
  • video/mp4
  • video/webm
  • audio/mpeg
  • audio/ogg
  • audio/mp4

Strangely, canPlayType() returns a string and not a boolean as you would expect. It can be one of three values:

  • "probably" - the browser most likely supports this audio/video type
  • "maybe" - the browser might support this audio/video type
  • "" - (empty string) the browser does not support this audio/video type

A lot of developers have wondered why the empty string. My personal opinion is that it supports the use of a truthy/falsy test on the results, as in if player.canPlayType(type) doSomething(). It's also easily added to a message like so:

function playSelectedFile(event) {
    var file    = this.files[0],
        type    = file.type,
        canPlay = player.canPlayType(type),
        message = 'Can play type "' + type 
                + '": ' + (canPlay ? canPlay : 'no');
                
    displayMessage(message, canPlay);
    
    if (canPlay) player.src = URL.createObjectURL(file);
    else         resetPlayer();
}

Here's the displayMessage() code. It sets the message text and element class:

function displayMessage(message, canPlay) {
    var element = document.querySelector('#message');
    element.innerHTML = message;
    element.className = canPlay ? 'info' : 'error';
}

Here's what comes up if you try to select a non-playable file:

player_with_no_message (42K)

Choosing a playable file results in the following message:

player_with_maybe_message (41K)

We can't get a response of "probably" from canPlayType() because it would require details about a specific codec such as "audio/ogg; codecs=vorbis" along with the file type (which we are not supplying) to say for sure whether or not the browser can play that type. And even then it only goes as far as "probably"!

The CSS Styling

It takes some fairly heavy-duty CSS to make our player look so stylish! Then again, a lot of it is duplication for various browser vendors.

/* General page stuff */
#audio_player, .info, .error, input {
  display: block;
  width: 427px;
  margin: auto auto auto auto;
}

.info {
    background-color: aqua;     
}

.error {
    background-color: red;
    color: white;
}

/* Audio player styles start here */
#audio_player {
  padding:2px 2px 1px 2px;
  background-color:black;
  border:2px solid darkgreen;
  border-radius: 9px;
  margin-top: 30px;
}

button {
  text-indent:-9999px;
  width:16px;
  height:18px;
  border:none;
  cursor:pointer;
  background:transparent url('buttons.png') no-repeat 0 0; 
}

.pause { background-position:-19px 0; }
.stop { background-position:-38px 0; }

#volume-bar {
  float:right;
  width: 80px;
  padding:0px 25px 0px 0px;
}
.mute { background-position:-95px 0; }
.unmute { background-position:-114px 0; }
.replay { background-position:-133px 0; }

progress {
  color: green;
  font-size: 12px;
  width: 220px;
  height: 12px;
  border: none;
  margin-right: 10px;
  background: #434343;
  border-radius: 9px;
}
progress::-moz-progress-bar { 
  color:green;
  background:#434343; 
}

progress[value]::-webkit-progress-bar {
  background-color: #434343;
  border-radius: 2px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25) inset;
}

progress[value]::-webkit-progress-value {
  background-color: green;
}

input[type=range] {
  -webkit-appearance: none;
  width: 100%;
  margin: 6.8px 0;
}
input[type=range]:focus {
  outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
  width: 100%;
  height: 4.4px;
  cursor: pointer;
  box-shadow: 0.9px 0.9px 1.7px #002200, 0px 0px 0.9px #003c00;
  background: #205928;
  border-radius: 1px;
  border: 1.1px solid #18d501;
}
input[type=range]::-webkit-slider-thumb {
  box-shadow: 2.6px 2.6px 3.7px #00aa00, 0px 0px 2.6px #00c300;
  border: 2.5px solid #83e584;
  height: 18px;
  width: 9px;
  border-radius: 3px;
  background: #439643;
  cursor: pointer;
  -webkit-appearance: none;
  margin-top: -7.9px;
}
input[type=range]:focus::-webkit-slider-runnable-track {
  background: #276c30;
}
input[type=range]::-moz-range-track {
  width: 100%;
  height: 4.4px;
  cursor: pointer;
  box-shadow: 0.9px 0.9px 1.7px #002200, 0px 0px 0.9px #003c00;
  background: #205928;
  border-radius: 1px;
  border: 1.1px solid #18d501;
}
input[type=range]::-moz-range-thumb {
  box-shadow: 2.6px 2.6px 3.7px #00aa00, 0px 0px 2.6px #00c300;
  border: 2.5px solid #83e584;
  height: 18px;
  width: 9px;
  border-radius: 3px;
  background: #439643;
  cursor: pointer;
}
input[type=range]::-ms-track {
  width: 100%;
  height: 4.4px;
  cursor: pointer;
  background: transparent;
  border-color: transparent;
  color: transparent;
}
input[type=range]::-ms-fill-lower {
  background: #194620;
  border: 1.1px solid #18d501;
  border-radius: 2px;
  box-shadow: 0.9px 0.9px 1.7px #002200, 0px 0px 0.9px #003c00;
}
input[type=range]::-ms-fill-upper {
  background: #205928;
  border: 1.1px solid #18d501;
  border-radius: 2px;
  box-shadow: 0.9px 0.9px 1.7px #002200, 0px 0px 0.9px #003c00;
}
input[type=range]::-ms-thumb {
  box-shadow: 2.6px 2.6px 3.7px #00aa00, 0px 0px 2.6px #00c300;
  border: 2.5px solid #83e584;
  height: 18px;
  width: 9px;
  border-radius: 3px;
  background: #439643;
  cursor: pointer;
  height: 4.4px;
}
input[type=range]:focus::-ms-fill-lower {
  background: #205928;
}
input[type=range]:focus::-ms-fill-upper {
  background: #276c30;
}

Conclusion

In the next article, we'll bind the controls to some JavaScript code to make our audio player work. In the meantime, feel free to try out the demo on Codepen. It's fully functioning, so you can get a sneak peak at part 2's code.



Rob Gravelle

Rob Gravelle resides in Ottawa, Canada. His design company has built web applications for numerous businesses and government agencies. Email him.

Rob's alter-ego, "Blackjacques", is an accomplished guitar player, who has released several CDs and cover songs. His band, Ivory Knight, was rated as one of Canada's top hard rock and metal groups by Brave Words magazine (issue #92).



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •  
  •  
Thanks for your registration, follow us on our social networks to keep up-to-date