Using Media Events to Add a Progress Bar

The media object provides a rich selection of events that the audio object can use. With one group of events, you can get status data that can be used to track the progress as an audio file plays.

  • Using status events
  • Adding a progress bar
  • Related topics

Using status events

The HTML5 media object provides many methods, properties, and events that are shared with the audio and video objects. In this topic, you learn to register several media events to track the progress of an audio file that is currently in play mode.

Building on the example from Using JavaScript to Control the Audio Object, you can use events to display the status of the file, one to drive the progress bar, and one to load the events when the webpage has finished loading.

The following example shows the events that fire when the file is playing and when it pauses. The events are used to toggle the label on the Play button. While an audio file is playing, the button label displays "Pause", and when the file is paused, the button label shows "Play".

Note  If you are developing on an intranet and have rendering issues for HTML5, you can add <meta http-equiv-“X-UA-Compatible” content=”IE=9”/> to the <head> block of a webpage to force Windows Internet Explorer 9 to use the latest standards. If you prefer, configure your web development server to send a meta http-equiv-“X-UA-Compatible” header with IE=9 instead. For more information about document compatibility, see Defining Document Compatibility.

 

                var oAudio = document.getElementById('myaudio');

                //set up event to toggle play button to pause when playing
                oAudio.addEventListener("playing", function() {
                    document.getElementById("play").textContent = "Pause";
                }, true);

                //set up event to toggle play button to play when paused
                oAudio.addEventListener("pause", function() {
                    document.getElementById("play").textContent = "Play";
                }, true);

In addition to the status events, the DOMContentLoaded event is registered when the script first loads. This is a new event for Internet Explorer 9, which fires when all Document Object Model (DOM) and script content has been loaded. By using this event, you can ensure that the elements you need are loaded, but not wait for images and content to load. When the event fires, it calls the function that loads the other events.

            //this event gets fired when a page has loaded
            window.addEventListener("DOMContentLoaded", initEvents, false);

The functions that are called skip forward and backward are described in Using JavaScript to Control the Audio Object.

Adding a progress bar

As seen in the Using JavaScript to Control the Audio Object topic, the audio object uses the currentTime property to track where the playback currently is. You were able to seek through the currentTime audio file by resetting the currentTime property. By using the timeupdate event, the time can be obtained and displayed as it changes. The currentTime property can also be used to update a progress bar.

The timeupdate event occurs every time the currentTime changes while an audio file is playing. The following example registers the timeupdate event, which is fired when currentTime changes.

                //set up event to update the progress bar
                oAudio.addEventListener("timeupdate", progressBar, true); 

The timeupdate event calls the progressBar function when it fires, which then updates the progress bar.

The progressBar function first gets a value for elapsedTime by getting and rounding currentTime to the nearest integer by using the Math.round method. Rounding this value is not required for our progress bar, however, it is recommended to avoid problems when printing.

            function progressBar() { 
                var oAudio = document.getElementById('myaudio'); 
                //get current time in seconds
                var elapsedTime = Math.round(oAudio.currentTime);
                //update the progress bar
                if (canvas.getContext) {
                    var ctx = canvas.getContext("2d");
                    //clear canvas before painting
                    ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
                    ctx.fillStyle = "rgb(255,0,0)";
                    var fWidth = (elapsedTime / oAudio.duration) * (canvas.clientWidth);
                    if (fWidth > 0) {
                        ctx.fillRect(0, 0, fWidth, canvas.clientHeight);
                    }
                }
            }

The progress bar uses the canvas element to create a rectangular area that is filled progressively to show the current position in the audio file. Canvas is anHTML5 element that provides a rich graphic area that has methods and properties that you can use to draw graphics or display images. When you use a canvas element, it is recommended to test for the feature before you use it. The canvas element, like the audio element, enables you to put failover text or code in the HTML portion of your page.

        <audio id="myaudio">
            HTML5 audio not supported
        </audio>
            <canvas id="canvas" width="500" height="20">
                canvas not supported
            </canvas>

In the JavaScript portion of the example, the code tests for the canvas object, which is returned by document.getElementById(). If the canvas tag is supported, you get an object; otherwise, it returns undefined. The JavaScript code then checks for getContext method support, and calls it to return a contextRenderingContext2D object into the "ctx" variable. Currently only 2d contexts are supported in Internet Explorer 9, but the W3C has allowed other types of contexts.

The contextRenderingContext2D object contains the majority of drawing and graphic methods and properties that you use to create graphics and display images on the canvas element. At each call to the progressBar function, the canvas is cleared to prevent the wrong display if the user has moved backward in the file. The width of the fill is calculated by dividing the currentTime value by the duration property of the audio object. The result is then multiplied by the clientWidth of the canvas to give a percentage of the width of the canvas. That value is then used in the fillRect method to fill the canvas.

Unless a canvas has some graphics on it, it has no set color. In the following example, you use a Cascading Style Sheets (CSS) style to outline the canvas progress bar. Without the outline, you do not see the bar until your file starts to play.

The progress bar can also be used to move within the audio file. A mouse click event is registered to a function that updates the currentTime in a position relative to where you clicked. When you click the progress bar, it calculates and sets currentTime based on the relative X position of the cursor within the canvas, as shown in the following example.

                //set up mouse click to control position of audio
                canvas.addEventListener("click", function(e) {
                    //this might seem redundant, but this these are needed later - make global to remove these
                    var oAudio = document.getElementById('myaudio'); 
                    var canvas = document.getElementById('canvas');            

                    if (!e) {
                        e = window.event;
                    } //get the latest windows event if it isn't set
                    try {
                        //calculate the current time based on position of mouse cursor in canvas box
                        oAudio.currentTime = oAudio.duration * (e.offsetX / canvas.clientWidth);
                    }
                    catch (err) {
                    // Fail silently but show in F12 developer tools console
                        if (window.console && console.error("Error:" + err));
                    }
                }, true);
            }

The previous functions and code examples show the basics of how to play an audio file on a webpage, display a progress bar, and control the position of playback of an audio file. The following examples use discrete calls to get the canvas and audio objects each time they are needed. In your own code, you might want to create global variables, or even encapsulate the player into its own global object.

The player can be expanded to use a playlist, to display changing images as music plays, to use multiple audio elements, or create virtual instruments by using sampled audio.

The following example contains all the code examples in this topic and combines them to show a complete process. Copy and paste this to an HTML file, and start experimenting.

<!DOCTYPE html>
<html>
    
    <head>
         
        <title>Using media events to add a progress bar to an audio player</title>
        <!-- Uncomment the following meta tag if you have issues rendering this page on an intranet site. -->    
        <!--  <meta http-equiv="X-UA-Compatible" content="IE=9"/> --> 
        <style id="inlinecss" type="text/css">
            /* put a border around the canvas element */
            #canvas  
            {
                margin-top:10px; 
                border-style:solid; 
                border-width:1px; 
                padding:3px; 
            }
        </style>
        <script type="text/javascript">
            //Global variable to track current file name        
            var currentFile = "";

            //display and update progress bar
            function progressBar() { 
                var oAudio = document.getElementById('myaudio'); 
                //get current time in seconds
                var elapsedTime = Math.round(oAudio.currentTime);
                //update the progress bar
                if (canvas.getContext) {
                    var ctx = canvas.getContext("2d");
                    //clear canvas before painting
                    ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
                    ctx.fillStyle = "rgb(255,0,0)";
                    var fWidth = (elapsedTime / oAudio.duration) * (canvas.clientWidth);
                    if (fWidth > 0) {
                        ctx.fillRect(0, 0, fWidth, canvas.clientHeight);
                    }
                }
            }
            //Play and pause function 
            function playAudio() {
                try {
                    //return objects we need to work with 
                    var oAudio = document.getElementById('myaudio'); 
                    var btn = document.getElementById('play');
                    var audioURL = document.getElementById('audiofile');               

                    //Skip loading if current file hasn't changed.
                    if (audioURL.value !== currentFile) {
                        oAudio.src = audioURL.value;
                        currentFile = audioURL.value;                        
                    }

                    //Tests the paused attribute and set state. 
                    if (oAudio.paused) {
                        oAudio.play();
                        btn.textContent = "Pause";
                    }
                    else {
                        oAudio.pause();
                        btn.textContent = "Play";
                    }
                }
                catch (e) {
                    // Fail silently but show in F12 developer tools console
                    if (window.console && console.error("Error:" + e));
                }
            }

            //Rewinds the audio file by 30 seconds.
            function rewindAudio() {
                try {
                    var oAudio = document.getElementById('myaudio');
                    oAudio.currentTime -= 30.0;
                }
                catch (e) {
                    // Fail silently but show in F12 developer tools console
                    if (window.console && console.error("Error:" + e));
                }
            }

            //Fast forwards the audio file by 30 seconds.
            function forwardAudio() {
                try {
                    var oAudio = document.getElementById('myaudio');
                    oAudio.currentTime += 30.0;
                }
                catch (e) {
                    // Fail silently but show in F12 developer tools console
                    if (window.console && console.error("Error:" + e));
                }
            }

            //Restart the audio file to the beginning.

            function restartAudio() {
                try {
                    var oAudio = document.getElementById('myaudio');
                    oAudio.currentTime = 0;
                }
                catch (e) {
                    // Fail silently but show in F12 developer tools console
                    if (window.console && console.error("Error:" + e));
                }
            }

            //added events

            function initEvents() {
                var canvas = document.getElementById('canvas');  
                var oAudio = document.getElementById('myaudio');

                //set up event to toggle play button to pause when playing
                oAudio.addEventListener("playing", function() {
                    document.getElementById("play").textContent = "Pause";
                }, true);

                //set up event to toggle play button to play when paused
                oAudio.addEventListener("pause", function() {
                    document.getElementById("play").textContent = "Play";
                }, true);
                //set up event to update the progress bar
                oAudio.addEventListener("timeupdate", progressBar, true); 
                //set up mouse click to control position of audio
                canvas.addEventListener("click", function(e) {
                    //this might seem redundant, but this these are needed later - make global to remove these
                    var oAudio = document.getElementById('myaudio'); 
                    var canvas = document.getElementById('canvas');            

                    if (!e) {
                        e = window.event;
                    } //get the latest windows event if it isn't set
                    try {
                        //calculate the current time based on position of mouse cursor in canvas box
                        oAudio.currentTime = oAudio.duration * (e.offsetX / canvas.clientWidth);
                    }
                    catch (err) {
                    // Fail silently but show in F12 developer tools console
                        if (window.console && console.error("Error:" + err));
                    }
                }, true);
            }
            //this event gets fired when a page has loaded
            window.addEventListener("DOMContentLoaded", initEvents, false);
        </script>
    </head>    
    <body>
        <h1>HTML5 audio player with progress bar</h1>
        <p>
            <input type="text" id="audiofile" size="80" value="demo.mp3" />
        </p>
        <audio id="myaudio">
            HTML5 audio not supported
        </audio>
        <p>
            <button id="play" onclick="playAudio();" disabled>
                Play
            </button>
            <button id="rewind" onclick="rewindAudio();" disabled>
                Rewind
            </button>
            <button id="forward" onclick="forwardAudio();" disabled>
                Fast forward
            </button>
            <button id="restart" onclick="restartAudio();" disabled>
                Restart
            </button>
        </p>
        <p>
            <canvas id="canvas" width="500" height="20">
                canvas not supported
            </canvas>
        </p>
        <script type="text/javascript">
            //Check for support and enable buttons
            if (window.HTMLAudioElement) {
                document.getElementById("play").disabled = false;
                document.getElementById("rewind").disabled = false;
                document.getElementById("forward").disabled = false;
                document.getElementById("restart").disabled = false;
            }
        </script>
    </body>

</html>

Note  Some functions in this example contain try/catch statements to silently catch exceptions that can occur, such as trying to skip forward with no file loaded. Code is included to also send errors to the F12 developer tools Console or Script tab. For more information, see Using the F12 Tools Console to View Errors and Status

 

Getting Started with the HTML5 Audio Element

How to use HTML5 to Add an Audio Player to your Webpage

Using JavaScript to Control the Audio Object