How to Build Spotify Audio Controls in JavaScript

If you’ve been on the web since the mid 1990’s you probably remember the annoying songs and sounds that would blast through your speakers whenever you loaded a new page. Thankfully the default for most browsers is to prevent this autoplay functionality, but audio and specifically music are still a huge part of today’s internet.

Today we are going to look somewhat in depth into the Audio Web API, the HTML5 audio tag and finally how to use these two items to create our own visually customizable audio players.

Audio API

JavaScript offers a number of cross browser Web APIs to allow us to interact with high level coding concepts. A popular API for websites offering music services is the Audio API. This object constructor accepts an optional url to an audio file and returns a new HTMLAudioElement that can be used to check track duration, currentTime, volume, offers pause and play functions and checks for different states such as ended and paused.

This information is great if you are looking to manipulate or update your pages based on the state of your audio file. What should we do if we want to use this API in our webpages?

Audio HTML Element

The release of HTML5 added a number of semantic tags to our arsenal that offer meaning to our web document’s structure. It also added a number of functional tags for bringing our pages to life. In order to avoid attaching a separate script to your page, HTML5 offers a new <audio> tag that accepts a source attribute for your audio file and returns a new instance of the HTMLAudioElement object.

The default behavior of the audio tag is to not be visible on the screen. However, the audio tag has a popular attribute of ‘controls’ which will display a pause/play button, progress bar and volume controls:

While this is great for a quick and dirty MVP, the visuals of the default controls aren’t great. Making this even more of an issue is that the display is not uniform across browsers and there is very little CSS that can target and alter the display characteristics of this player.

If you need something that works, but don’t care how it looks, an <audio> tag with the controls attribute will work great. If you do care about how your UI looks, let’s look at how to create your custom audio player!

Building our Audio Player

As mentioned previously, the default audio UI offered by the <audio> tag is functional, but not always visually appealing. We can change that by hooking into the our HTMLAudioElement instance. For this tutorial I would like to use Vanilla JavaScript with HTML and CSS. The beautiful thing about web development is there are so many different ways to create this audio player. Another solution (link in the notes) is to use React and tie into the player using React Hooks and yet another solution I have implemented involved using NextJS and Typescript to create a hydrated player (though you have to be careful because Web APIs are not available in Node).

There are two main approaches to building a custom UI audio player with Vanilla JavaScript. The first is to create the audio object using the <audio> tag and accessing it in your JS file by hooking into its class or id. The second is to create an instance of the Audio object in JavaScript and then manipulating HTML tags from the script file.

For our solution we will add an <audio> tag to our index.html and then access it through our script.js file. To start, clone my project repository ( Change into the ‘starter_files’ directory and open the HTML file. The current file should have a body containing only a blank <audio> tag. The mp3 file for our song is stored in the ‘assets’ directory. We will set the src attribute of our audio tag to “/final_files/assets/indie_song.mp3” which will instantiate a new Audio object with our song.

The final changes we will do with this HTML file are to add the container and structure elements for our audio player. Because we aren’t using the default audio UI by including ‘controls’, we will need to create our own UI. Here is what the body of our HTML file should look like at this point:

Our styling for this audio player isn’t anything too special, so for brevity I will skip going through the file in depth. Just know that I did not take the time to make this responsive, so if you would like to improve the app please feel free to add some min and max widths to the components and potentially change the flex direction to stack the controls of the player for mobile devices. As it is now our current player looks like this:

Now we have the HTML structure, the CSS styling and an HTMLAudioElement instance containing our indie song’s mp3 file. Now we need to add some functionality to our file. To start, let’s hook into the different element classes and save these references as variables. I assigned the play/pause button, the volume controls and finally the track progress indicator. At the top of our script.js file we use document.querySelector() to grab each of these values, including accessing our Audio object instance.

We can interact and change these elements now, so let’s set up some event handlers to watch for clicks. The easiest functionality to add is the volume controls. With our two separate buttons, we will add event handlers that increment and decrement the volume of our Audio object to a certain point:

The next functionality we’d like to add is playing and pausing the track. The Audio object has built in play() and pause() methods and we are able to access this object from our ‘audioEl’ variable we declared at the top of our file. Let’s add an event listener to the playButton that will handle starting and stopping the music:

The Audio element has a number of boolean flags set on it so that we don’t have to be taxed with creating our own flags for object state. Along with the ‘paused’ flag, we can make use of common tags such as ended. With this all we are doing is checking if the audio is paused and calling it if is, otherwise we pause it. I am using web unicodes for the play/pause icons so I have to adjust the fontSize, but you can get the same effects using a font awesome font or SVGs.

With our volume controls and play/pause set, we need to have a visual cue for the user for track progression. Sure we could access the audioEl.duration and use setInterval() to get and display audioEl.currentTime every 1000 ms but that’s not very pretty. Instead let us change the position of the ball on the track bar every second the song plays. For this we will need to get the time elapsed every second and use this with our track duration to create a percentage of pixels traversed for our progress indicator. That will look like this:

Here we set an interval on our page to get the percentagePlayed or percent of the elapsedTime in relation to the songs duration. We then can set the left offset on our progress element relative to its container which will display to the user how far through the song they are.

To make this better we could have a state variable to store the interval ID returned when we start the interval and then call clearInterval(audioIntervalId) every time we call, but I’ll leave that to you. And with that we have a functional audio player with custom styling!


Now you have a basic understanding of the Audio Web API, the HTML5 <audio> tag and a simple approach to building a custom audio player and UI. If you want to continue to learn, you can try adding the Drag and Drop API to allow for audio scrubbing (start with adding ‘draggable=true’ to the div with a class of ‘progress’), add cool visuals to background of the audio player container or even perform visualizations by digging deeper into the Web Audio API. Let me know in the comments how you plan to build on our basic player!




Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Daniel Pericich

Daniel Pericich

Former Big Beer Engineer turned Full Stack Software Engineer