Building a Mobile JavaScript Powered Audio Player

Share this article

As some of you might know, I’m addicted to HTML5 and JavaScript APIs.

I’ve written many tutorials discussing APIs such as the getUserMedia API, the Web Speech API, the Screen Orientation API, and I also have a dedicated repository on GitHub.

In this article I’ll show you how to create a simple JavaScript API-powered audio player that employs several APIs to improve the experience for people using a mobile device.

A JavaScript API Powered Audio Player

In this tutorial I’ll use the following APIs:

  • The Ambient Light API to change the theme of the web page based on the light level of the surrounding environment.
  • The Proximity API to play/pause audio based on the proximity of an object.
  • The Battery Status API to detect the battery level and automatically pause audio when the battery is running critically low.
  • The Web Notifications API to notify the user when the battery is running low and that the audio was paused because of this.
  • The Vibration API to provide tactile feedback that reinforces the notification message described above.

If you need a refresher of one or more of these APIs, take a look at the articles linked because this tutorial will assume that you know how to work with them.

This demo will use the native HTML5 audio element to play audio without any library as a fallback. However, a message will be shown in case the browser doesn’t support the audio element.

The whole application will be developed with progressive enhancement in mind. If a browser doesn’t support one or more of the previously cited APIs, the application will continue to work properly. The only difference is that the browser won’t take advantage of the feature that employs the unsupported API.

Creating the markup

The demo is made of a single HTML page with very simple markup. It’s made of a short summary of the experiment and the audio element with the native controls enabled (controls attribute). The page has a link and a script element. The former refers to the CSS file (discussed in the next section) containing the declaration blocks to define the three different themes (more on this in the next section). The latter points to the JavaScript file containing the business logic of the experiment.

As you can see in the code below, the body element has a predefined class attribute’s value of normal-theme. It represents the default theme that is used under normal light conditions.

The full code of the HTML page is listed below:

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title>Mobile Audio Player</title>
      <meta name="description" content="APIs-powered Audio Player">
      <meta name="viewport" content="width=device-width, initial-scale=1">

      <link rel="stylesheet" href="css/main.css">
   </head>
   <body class="normal-theme">
      <h1>APIs-powered Audio Player</h1>
      <p>
         This demo shows how to create a simple APIs-powered audio player. In particular this page
         uses the Proximity API, the Battery Status API, the Vibration API, the Web Notifications API,
         and the Ambient Light API.
      </p>
      <audio id="audio" src="http://freshly-ground.com/data/audio/mpc/20090119%20-%20Untitled%20Groove.mp3" controls>
         <p>I'm sorry but your browser doesn't support the <code>audio</code> element, so you can't run the demo.</p>
      </audio>

      <script src="js/main.js" async></script>
   </body>
</html>

The Player themes

The CSS file of the experiment is very simple and short. It defines several rules for the body element and three themes: dark-theme, normal-theme, and light-theme. Each of these themes define a color for the background and one for the text of the page.

In a dark environment, to avoid causing stress to the eyes of the users we’ll set a dark background with a light color for the text. Conversely, in a bright environment, we’ll adopt a light background with a dark color for the text. Under normal light conditions (the default) we use the combination that we like the most, provided that our users are still able to use the page (for example, red text on a red background isn’t a good choice).

The full CSS code is below:

body
{
   max-width: 600px;
   margin: 0 auto;
   font-size: 20px;
   padding: 0 1em;
}

.dark-theme
{
   background-color: #000000;
   color: #FFFFFF;
}

.normal-theme
{
   background-color: #B8FFF7;
   color: #C53131;
}

.light-theme
{
   background-color: #FFFFFF;
   color: #000000;
}

The business logic

The business logic is the most exciting part of this experiment. Here we’ll cover the code that powers the audio player and how to build the features described at the beginning of the article.

The first step we need to perform is to test the support for the APIs we plan to use and store the results as properties of a literal object:

var tests = {
   proximity: 'onuserproximity' in window,
   light: 'ondevicelight' in window,
   vibration: 'vibrate' in window.navigator,
   notification: 'Notification' in window
};

As you can see the test for the Battery Status API is missing. Firefox implements an old version of the specifications that isn’t Promise-based, thus we’ll treat this API as a case on its own. In this demo I wanted to support both versions because Firefox is the only browser that implements all the APIs employed in this experiment. I thought it was important to have at least one browser able to expose all the features of the demo.

In addition to the test variable, we also need a config variable defined as follows:

var config = {
   battery: {
      lowThreshold: 0.15,
      criticalThreshold: 0.05
   },
   vibration: {
      lowThreshold: [500, 200, 500],
      criticalThreshold: [1000]
   },
   notification: {
      lowThreshold: {
         tTitle: 'Battery level: low',
         message: 'Please charge your device to avoid the audio to be automatically paused.'
      },
      criticalThreshold: {
         title: 'Battery level: critical',
         message: 'The audio has been stopped to avoid the shutdown of your device.'
      }
   },
   light: {
      darkThreshold: 50,
      normalThreshold: 10000
   }
};

It contains data that we’ll use in combination with the JavaScript APIs. For example, we have defined the thresholds to use with the Battery Status API (under the battery property) to specify when our application will consider the battery level to be low or critical. We have also defined the vibration patterns (under the vibration property) to use when the battery level is low (lowThreshold) or critical (criticalThreshold). Finally, we have defined properties to employ with the Web Notifications API (notification property) and the Ambient Light API (light property), to specify when we’ll consider the light level to be low and normal.

The last step of preparation we need to perform is to retrieve the audio element. This is achieved with the following statement:

var audio = document.getElementById('audio');

At this point we’re ready to employ the JavaScript APIs to give superpowers to our audio player. The first feature we’ll implement is the gesture to play/pause the audio. To be precise, we’ll not implement a real gesture. Placing a finger, the hand, or any other object close enough to the proximity sensor will be enough to play/pause the audio, but calling it a “gesture” sounds better.

This feature is implemented with the following code:

if (tests.proximity) {
   window.addEventListener('userproximity', function (event) {
      if (event.near) {
         audio.paused ? audio.play() : audio.pause();
      }
   });
}

Easy, isn’t it? Another easy feature we can create is to switch the theme applied based on the environment light level. Listening for a change of the light level, we can detect if it’s below the darkness threshold defined (darkThreshold), between the latter and the normal threshold (normalThreshold), or above the normal threshold. Once done, we can change the theme accordingly. Turning this description into code results in the following snippet:

if (tests.light) {
   window.addEventListener('devicelight', function(event) {
      var light = Math.round(event.value);

      if (light < config.light.darkThreshold) {
         document.body.className = 'dark-theme';
      } else if (light < config.light.normalThreshold) {
         document.body.className = 'normal-theme';
      } else {
         document.body.className = 'light-theme';
      }
   });
}

Now that we’ve got the code to detect the change of the light level and to detect the “gesture” to play/pause the audio, we have to implement the features related to the battery level. To do that we have to attach a handler to the levelchange event and run the same handler as soon as the application starts. Doing so, if the battery level is in a low or critical state when the application starts we’ll be able to act accordingly. For this purpose, we’ll define a manageBattery() function. We’ll also detect the version of the Battery Status API supported by the browser to know if we can attach the handler directly or when the Promise is resolved.

The resulting code is listed below:

function manageBattery(battery) {
   // Code here...
}

if (window.navigator.getBattery) {
   window.navigator.getBattery().then(function(battery){
      battery.addEventListener('levelchange', manageBattery.bind(window, battery));
      manageBattery(battery);
   });
} else if (window.navigator.battery) {
   window.navigator.battery.addEventListener('levelchange', manageBattery.bind(window, window.navigator.battery));
   manageBattery(window.navigator.battery);
}

The last step to perform is to create the body of the manageBattery() function. Inside this function we have to perform the following operations:

  1. Detect the battery level (good, low, or critical)
  2. Pause the audio if the battery level is critical
  3. Vibrate the device using a different pattern depending on the battery level (low or critical)
  4. Show a different notification message about the state of the battery depending on its level (low or critical)

Based on this list, the resulting code is as follows:

function manageBattery(battery) {
   if(!battery.charging && audio.duration > 0 && !audio.paused) {
      if (battery.level > config.battery.lowThreshold) {
         return;
      }

      var isCritical = battery.level <= config.battery.criticalThreshold;
      if (isCritical) {
         audio.pause();
      }

      if (tests.vibration) {
         window.navigator.vibrate(
            isCritical ? config.vibration.criticalThreshold : config.vibration.lowThreshold
         );
      }

      if (tests.notification) {
         Notification.requestPermission(function(permission) {
            if (permission !== 'denied') {
               new Notification(
                  isCritical ?  config.notification.criticalThreshold.title : config.notification.lowThreshold.title,
                  {
                     body: isCritical ?
                        config.notification.criticalThreshold.message :
                        config.notification.lowThreshold.message
                  }
               );
            }
         });
      }
   }
}

With this last snippet we’ve finished our demo and it is now ready to be tested.

Code and live demo

The complete and working code for this experiment can be found on GitHub. If you want to see the code we’ve developed in action, a live demo is also available.

Conclusion

In this tutorial we’ve developed a simple yet functional audio player that used several new JavaScript APIs. With this experiment I’ve proved that using the JavaScript APIs you can create powerful applications that have mobile-focused features, and improve the experience of your users. I hope you liked it and you found this demo fun.

Frequently Asked Questions (FAQs) on Building a Mobile JavaScript-Powered Audio Player

How Can I Implement a Playlist Feature in My JavaScript Audio Player?

Implementing a playlist feature in your JavaScript audio player involves creating an array of audio files and a function to cycle through them. You can create an array of audio files using the new Audio() constructor. Then, create a function that increments a counter each time an audio file ends, using the ‘ended’ event listener. This counter can then be used as an index to access the next audio file in the array. Remember to reset the counter when it reaches the end of the array to loop the playlist.

How Can I Add Custom Controls to My JavaScript Audio Player?

Custom controls can be added to your JavaScript audio player by creating HTML elements and attaching event listeners to them. For example, you can create a play button using a div element and attach a ‘click’ event listener to it. Inside the event listener, you can use the play() method of the Audio object to start playing the audio. Similarly, you can create pause, stop, next, and previous buttons.

How Can I Make My JavaScript Audio Player Responsive?

Making your JavaScript audio player responsive involves using CSS media queries to adjust the layout and design based on the screen size. You can define different styles for different screen sizes using the @media rule. For example, you can adjust the size and position of the audio player and its controls for mobile, tablet, and desktop screens.

How Can I Use the Web Audio API in My JavaScript Audio Player?

The Web Audio API provides a powerful and versatile system for controlling audio on the web. You can use it in your JavaScript audio player to control audio playback, volume, and even apply audio effects. To use the Web Audio API, you need to create an AudioContext object, which represents an audio-processing graph. You can then create audio source nodes from your audio files and connect them to the AudioContext to start playing the audio.

How Can I Add a Progress Bar to My JavaScript Audio Player?

A progress bar can be added to your JavaScript audio player by creating an HTML element and updating its width based on the current playback time. You can use the currentTime and duration properties of the Audio object to calculate the current progress as a percentage. Then, use this percentage to update the width of the progress bar element.

How Can I Add a Volume Control to My JavaScript Audio Player?

A volume control can be added to your JavaScript audio player by creating an HTML input element of type range and attaching an input event listener to it. Inside the event listener, you can use the value of the input element to set the volume property of the Audio object.

How Can I Add a Mute Button to My JavaScript Audio Player?

A mute button can be added to your JavaScript audio player by creating an HTML button element and attaching a click event listener to it. Inside the event listener, you can use the muted property of the Audio object to mute and unmute the audio.

How Can I Add a Loop Button to My JavaScript Audio Player?

A loop button can be added to your JavaScript audio player by creating an HTML button element and attaching a click event listener to it. Inside the event listener, you can use the loop property of the Audio object to enable and disable looping.

How Can I Add a Shuffle Button to My JavaScript Audio Player?

A shuffle button can be added to your JavaScript audio player by creating an HTML button element and attaching a click event listener to it. Inside the event listener, you can create a function that randomizes the order of the audio files in your playlist.

How Can I Add a Download Button to My JavaScript Audio Player?

A download button can be added to your JavaScript audio player by creating an HTML anchor element with the download attribute. You can set the href attribute of the anchor element to the URL of the audio file and the download attribute to the desired filename. When the user clicks the download button, the browser will download the audio file.

Aurelio De RosaAurelio De Rosa
View Author

I'm a (full-stack) web and app developer with more than 5 years' experience programming for the web using HTML, CSS, Sass, JavaScript, and PHP. I'm an expert of JavaScript and HTML5 APIs but my interests include web security, accessibility, performance, and SEO. I'm also a regular writer for several networks, speaker, and author of the books jQuery in Action, third edition and Instant jQuery Selectors.

apichriswjavascriptmobile audio
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week