Saturday 10 November 2018

Make a HTML5 audio player from scratch | Complete Guide

Before HTML5, it was little more industrious to embed audio or video to the web page especially with custom controls.But now we have HTML5 and with its new video and audio tags, we can easily make a audio player.

Many days before I had written a post for how to make an HTML5 video player.Using the same HTML5 technique here, we will be making a beautiful HTML5 audio player!

Audio player, just like video player uses a <audio> tag.
make-a-html5-audio-player-from-scratch

Building HTML blocks

Apart from declaring <audio> tag in HTML and its attributes, now we'll look into scratch for custom audio player.
I've made it something like below.
Image showing scratch of audio player
Scratch of audio player
Now, At number 1 (refer to above image), there will be play/pause button, 2 - progress bar , 3 - time , 4- volume mute button and at 5 volume scrollbar for changing volume.

We will make total 4 <div> , except for time.For time, its good to use <span>.
In progress bar, there will be a ball which can be dragged to seek the audio and another bar to show the current timings.

The code after setting all the div's and after giving ids and classes looks like below.
<div id='audioplayermain'>
    <div id='playerboundry'>
        <div id='toggleplay' class='toggleplay'></div>
        <div id='progress'>
            <div id='seekbar' class='seekbar'></div>
            <div id='ball' class='ball' draggable='true'></div>
        </div>
        <span id='time'>00:00/00:00</span>
        <div id='volume' class='fullvolume'></div>
        <input type='range' max="100" min="0" id='volslider' value='100'>

    </div>
    <audio id='au' preload='metadata'>
   <source src="path/to/audio.ogg" type="audio/ogg">
   <source src="path/to/audio.mp3" type="audio/mpeg">
 Your browser does not support the audio element.
 </audio>
</div>

Preload attribute in audio means to browser that it should load the simple metadata of audio file when page loads.The possible values for preload attribute are - none, metadata and auto. Preload is not supported in IE.


Designing using CSS

I'm not gonna peep much into CSS matter more cos everyone may have their own ideas of designing and according to their ideas and colour scheme, they can give a whole different look to their HTML5 audio player.

Now, some basic styles I've declared to support our scratch.
#audioplayermain{
     display: block;
     margin-left: auto;
     margin-right: auto;
     height:50;
}
 #playerboundry{
     display:flex;
     justify-content: center;
     margin-left: auto;
     margin-right: auto;
     background-color:transparent;
     background-image: linear-gradient(transparent, black);
     height:60px;
     position:absolute;
}

For play pause button, two different styles are required when playing and when paused.We gonna just change the background picture.
.toggleplay{
     background-image:url('play-button-circle-icon.png');
     background-repeat:no-repeat;
     background-size: 60px;
     width:60px;
     position:relative;
}
 .toggleplaypause{
     width:60px;
     position:relative;
     background-image:url('pause-button-circle-icon.png');
     background-repeat:no-repeat;
     background-size: 60px;
}

For volume:
 .fullvolume{
     width:60px;
     background-image:url('volume-button-grey-circle-icon.png');
     background-repeat:no-repeat;
     background-size: 60px;
}
 .mute{
     width:60px;
     background-image:url('mute-icon.png');
     background-repeat:no-repeat;
     background-size: 60px;
}

And for rest of the <div>s
#progress{
     background-color:green;
     width:600px;
     position:relative;
     height:10px!important;
     margin-top:calc(60px/2 - 10px);
     margin-left:15px;
     margin-right:15px;
}
 #playerboundry > div {
     height:60px;
     display:block;
}
 #time{
     max-width:140px;
     margin-top: calc(60px/2 - 10px);
     margin-left:5px;
     margin-right:5px;
}
 .seekbar{
     width:0px;
     height: 10px;
     background-color: brown;
}
 .ball{
     height: 20px;
     width: 20px;
     background-color: aqua;
     color: black;
     border-radius: 100%;
     margin-top: -15px;
     margin-left:calc(-10px + 0%);
     cursor: grab;
}
 #volslider{
     -webkit-appearance: none;
     height: 10px;
     border-radius:3px;
     background: #d3d3d3;
     outline: none;
     opacity: 0.7;
     -webkit-transition: .2s;
     transition: opacity .2s;
     margin-top:20px;
}
 #volslider::hover{
     opacity:1;
}
 #volslider::-webkit-slider-thumb {
     -webkit-appearance: none;
     appearance: none;
     width: 20px;
     height: 20px;
     border-radius:100%;
     background:aqua;
     cursor: pointer;
}
 #volslider::-moz-range-thumb {
     width: 20px;
     height: 20px;
     border-radius:100%;
     background:aqua;
     cursor: pointer;
}

The webkit-slider-thumb and moz-range-thumb is for <input type='range'>


Make the player to function : JavaScript

Now here comes the main part of JavaScript.Our player is like a car without engine without JavaScript.
Our JavaScript code flow will be like-
Declaring Variables ➡️ Functions➡️Event Listeners.

So lets declare the variables one by one-
var au = document.getElementById("au");
var playbutton = document.getElementById("toggleplay");
var time = document.getElementById("time");
var seekbar = document.getElementById("seekbar");
var seekbarwidth = document.getElementById("seekbar").offsetWidth;
var ball = document.getElementById("ball");
var volume = document.getElementById("volume");
var volslider = document.getElementById("volslider");
It is important to note that the JavaScript should be loaded after all the divisions along with audio tag.Else you'll get the following error in console.
Uncaught TypeError: Cannot read property 'offsetWidth' of null
    at audio-player.html:120
To avoid such kinds on errors, put script tag at the end of the document.
OR
Another way is to wrap all the functions in a single function and load that single function after DOM successfully loads.

Now the most basic function-

Play/Pause HTML5 audio using JavaScript

We will use paused() , pause() and play() functions.
function playPause() {
    if (au.paused) {
        playbutton.className = 'toggleplaypause';
        au.play();
    } else {
        au.pause();
        playbutton.className = 'toggleplay';
    }
The above function means that-
function playPause() {
    if (audio is paused) then
        playbutton's className will be'toggleplaypause';
        and audio will start to play();
    else {
        audio will paused();
        and playbutton's className will be 'toggleplay';
    }
Just kidding! I think I could have done better if I used comments.

Well, lets move further.Next function is format time and timeupdate.
function formatTime(seconds) {
    seconds = Math.round(seconds);
    minutes = Math.floor(seconds / 60);
    minutes = (minutes >= 10) ? minutes : "0" + minutes;
    seconds = Math.floor(seconds % 60);
    seconds = (seconds >= 10) ? seconds : "0" + seconds;
    return minutes + ":" + seconds;
}

function timeupdate() {
    var ongoingtime = formatTime(au.currentTime);
    var totaltime = formatTime(au.duration);
    time.innerHTML = (ongoingtime + '/' + totaltime);
    seekbar.style.width = au.currentTime / au.duration * 100 + '%';
    ball.style.marginLeft = 'calc( -10px + ' + au.currentTime / au.duration * 100 + '% )';

    if (au.networkState === au.NETWORK_LOADING) {
        // The user agent is actively trying to download data.
        // The colour of the ball changes to red while buffering.
        ball.style.backgroundColor = 'red';
    } else {
        ball.style.backgroundColor = '';//returns to original colour
    }
}

Time update is most important function as it occurs when the playing position of an audio/video has changed.
When we use currentTime to get current time of playing audio, it returns in seconds.To convert it in minutes:seconds format, we have used beautiful time conversion function above.

Now to detect the buffering activity I had used
if (au.networkState === au.NETWORK_LOADING) {
You can add a loading gif that triggers when the audio is buffering. Here I simply made the ball red just as indication sign.

Changing The Progress Bar As Per User Drags It

function changeTime(event) {
    var clickedpos = event.clientX;
    var offsets = seekbar.getBoundingClientRect();
    //gets offsets of the seekbar
    var width = clickedpos - offsets.left;
    //so that we can find the difference(in px) in mouse drag
    au.currentTime = width * au.duration / 600;//changes current time
    ball.style.marginLeft = 'calc( -10px + ' + au.currentTime / au.duration * 100 + '% )';
    //ball moves forward before timeupdate function fires.
}
Change time function finds how much user dragged the ball.It finds it in px, converts into percentage and change the audio time as per that percentage.
This function will trigger when user clicks and drags the progress bar.

Change Volume in HTML5 Audio Player.

When we will click on the volume button, volume will muted.
function mute() {
    if (au.muted = !au.muted) {
        volume.className = 'mute';
        volslider.value = 0
    } else {
        volume.className = 'fullvolume';
        volslider.value = 100
    }
}

function changevol() {
    au.volume = volslider.value / 100;
    if (volslider.value == 0) {
        volume.className = 'mute';
    }
    if (volslider.value > 0) {
        volume.className = 'fullvolume';
    }
}
Changevol() will change the volume as user scrolls the range input.To detect when the volume changes, you can also use volume change event listener.
audio|video.addEventListener("volumechange",script);

Event Listeners For Audio Player

After declaring all the functions and variables, one thing seems to be missing.Event listeners will listen to the events and trigger the above functions.
The required event listeners are-
playbutton.addEventListener("click", playPause);
au.addEventListener("timeupdate", timeupdate);
progress.addEventListener("click", changeTime);
progress.addEventListener("mousedown", changeTime);
progress.addEventListener("dragend", changeTime);
progress.addEventListener("mouseup", changeTime);
volume.addEventListener("click", mute);
volslider.addEventListener("input", changevol);
After this crazy stuff, finally the audio player looks something like below.
HTML5 audio player
HTML5 audio player
If you want to see the live demo, see it here.

Anything Missing?

There are many things that you may seem missing.Many advancements can be done in this audio player.
You can make playlists, add thumbnails,  add playback speed feature,add keyboard functionality, add loading/ buffering GIF and add some cool animations
If you're wondering that some sites load audio with blob:// , you can do it too using the following snippet.
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';

xhr.onload = function() {
  
  var reader = new FileReader();
  
  reader.onloadend = function() {
  
    var byteCharacters = atob(reader.result.slice(reader.result.indexOf(',') + 1));
    
    var byteNumbers = new Array(byteCharacters.length);

    for (var i = 0; i < byteCharacters.length; i++) {
      
      byteNumbers[i] = byteCharacters.charCodeAt(i);
      
    }

    var byteArray = new Uint8Array(byteNumbers);
    var blob = new Blob([byteArray], {type: 'video/mpeg'});
    var url = URL.createObjectURL(blob);
    
    document.getElementById('au').src = url;
    
  }
  
  reader.readAsDataURL(xhr.response);
  
};

xhr.open('GET', 'path/to/file');
xhr.send();
If you're trying it with localhost, you  may get error like Origin http://localhost is not allowed by Access-Control-Allow-Origin. OR  from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. You can head over to solution here.

Adding Keyboard Function to Audio Player

I already made a post on detecting keyboard keys.Same function we are going to use here.
document.onkeydown = keyCtrl; //This will map all the keyboard actions.
function keyCtrl(e) { 
 if(e.keyCode ==32){playPause()}
 if(e.keyCode ==77){mute()}
 if(e.keyCode ==39){moveFor(10)}
 if(e.keyCode ==37){moveBac(5)}
}
Key code 32 , 77 , 39 and 37 are respectively for space, M , right arrow and left arrow.
Now moveFor and MoveBac are the functions to move audio forwards and backwards.
function moveFor(t){
au.currentTime = au.currentTime + t ;//add time t to current time
}

function moveBac(t){
au.currentTime = au.currentTime - t ;//subtract time t from to current time
}

I've made a playlist using this same player here- DEMO .If you have any problems, just comment down below and tag us on @css_magz.

0 Please Share a Your Opinion.:

Comment something useful and creative :)