Playing Audio on Websites

Introduction

Some of my pages have not aged well and a page I made about playing audio on web pages (Internet Archive) was worse than most. That page originally dealt mostly with MIDI files and was written in 2003, long before the <audio> and <audio> tags were introduced and MP3 and MP4 became the standard audio and video file formats across all browsers, around 2015.


Audio and Video Tags

The audio tag can play MP3, WAV, and OGG files, the video tag, MP4, WebM, and Ogg files.

Appearance

The basic appearance of the audio tag is set by the browser rendering engine. I use Chrome, Edge and Opera which are based on Chromium. I also use Firefox which is not. The simplest way to use the player is:

<audio controls src="gsmed.mp3"></audio>

Chromium audio tag

The audio tag in Chromium browsers
The tag looks the same in Chrome, Edge and Opera

Firefox audio tag

The audio tag in the Firefox browser

A problem for some is that the audio tag is not easily styled using CSS. THe following example uses:

<audio controls src="gsmed.mp3" style="background-color: red; color: blue;"></audio>

Chromium audio tag

The audio tag in Chromium browsers with simple CSS

Firefox audio tag

The audio tag in the Firefox browser

Neither of the above are quite what I expected.


CSS and Webkit

Normal CSS selectors do not have much effect on the audio tag. This is because it is prebuilt and part of the shadow DOM (Document Object Model). However, the webkit pseudo elements can be used to style various parts of the object.

Webkit has the following pseudo elements for the audio tag:

::-webkit-media-controls
::-webkit-media-controls-overlay-enclosure
::-internal-media-controls-overlay-cast-button
::-webkit-media-controls-enclosure
::-webkit-media-controls-panel
::-webkit-media-controls-play-button
::-webkit-media-controls-current-time-display
::-webkit-media-controls-time-remaining-display
::-webkit-media-controls-timeline
::-webkit-media-controls-volume-control-container
::-webkit-media-controls-volume-control-hover-background
::-webkit-media-controls-volume-slider
::-webkit-media-controls-mute-button
::-webkit-media-controls-fullscreen-button
::-internal-media-controls-overflow-button
::-internal-media-controls-text-track-list
::-internal-media-controls-playback-speed-list
::-internal-media-controls-video-track-selection-list
::-internal-media-controls-audio-track-selection-list
::-internal-media-controls-overflow-menu-list
::-internal-media-controls-overflow-menu-list-item
::-webkit-media-controls-play-button
::-internal-media-controls-overflow-menu-list-item
::-webkit-media-controls-fullscreen-button
::-internal-media-controls-overflow-menu-list-item
::-internal-media-controls-download-button
::-internal-media-controls-overflow-menu-list-item
::-webkit-media-controls-mute-button
::-internal-media-controls-overflow-menu-list-item
::-internal-media-controls-cast-button
::-internal-media-controls-overflow-menu-list-item
::-webkit-media-controls-toggle-closed-captions-button
::-internal-media-controls-overflow-menu-list-item
::-internal-media-controls-playback-speed-button
::-internal-media-controls-overflow-menu-list-item
::-internal-media-controls-video-track-selection-button
::-internal-media-controls-overflow-menu-list-item
::-internal-media-controls-audio-track-selection-button

The above are non-standard selectors and not all may be available in every browser.

The following CSS and webkit styling:

audio { border:10px solid red;
        border-radius: 50px;
}
audio::-webkit-media-controls-panel { background: linear-gradient(45deg, rgb(252, 114, 252), rgb(193, 113, 250), rgb(112, 112, 253), rgb(152, 252, 152), rgb(250, 250, 113), rgb(250, 202, 114), rgb(252, 114, 114))
}

produces:

Chromium audio tag with CSS and webkit styling

The audio tag in Chromium browsers with CSS and webkit styling

Firefox audio tag with CSS and webkit styling

The audio tag in the Firefox browser with CSS and webkit styling

Webkit only works on cartain browsers, the Chromimium based ones included. Firefox and other browsers use a moz enabled engine and I cannot find similar functionality to render the audio tag in that.


The Shadow DOM

Chromium Browsers

The shadow DOM encapsulates the premade elements of the DOM. In order to view the shadow DOM in Chromium based browsers, open the developer mode by usually pressing F12 or Ctrl + Shift + I.

In Chrome and Opera there is a gear icon in the top right-hand corner of the developer screen. In Edge, there is a 3-dot ellipsis. Click on that and choose Settings from the menu.

Once the developer settings menu is open, scroll down to Elements section and check the box that says "Show user agent shadow DOM"

The shadow DOM in Chromium browsers

The shadow DOM in Chromium browsers

Firefox Browser

In Firefox, open a new tab and in the address bar type:

about:config

In the search bar that opens, type:

devtools.inspector.showAllAnonymousContent

Then set the value of that to true.

The shadow DOM in Firefox

The shadow DOM in Firefox

Unfortunately, I cannot find a way to change the shadow DOM element appearance in Firefox.


JavaScript

The idea behind this method is that the audio tag controls are replaced by your own and then JavaScript is used to play the audio file.

The audio tag can be put on a webpage without any visible controls by simply using:

<audio src="gsmed.mp3"></audio>

This is pretty useless because the audio tag now cannot be seen at all. To make the player at least resemble something useful, what we need is a play/pause button, a time elapsed timer, a play position bar, and a volume control.

Luckily HTML already has all the tools that are needed such as buttons, sliders (input type="range"), and unicode characters. The following player is based on the code on Machinet (Internet Archive)and the HTML DOM Audio Object.

0:00 / 0:00  

Notice the difference in behaviour of the play/pause button and the mute button. The play/pause button stops playback of the audio file, the mute button does not.

Appearance

Not all browsers, even the ones based on Chromium, are the same. There are differences in how browsers render certain HTML elements.

How most browsers show the JS audio player

How Chrome, Edge, and Firefox show the JS audio player

How Opera shows the JS audio player

How Opera shows the JS audio player

The Code

CSS

<style>
		.audio-player {
    		width: 400px;
    		margin: 20px auto;
    		text-align: center;
    		background-color: #f3f3f3;
    		padding: 10px;
    		border:2px solid navy;
        	border-radius: 50px;
		}
		.controls {
    		display: flex;
    		align-items: center;
    		justify-content: space-between;
		}
		#play-pause, #mute, #download {
    		background-color: #007bff;
			border-radius: 50%;
			height: 30px;
			width: 30px;
    		color: white;
    		border: none;
    		font-size: 24px;
			line-height: 24px;
			align-items: center;
			padding: 0;
    		cursor: pointer;
		}
		#mute {
			font-size: 20px;
		}
		#seek-bar, #vol-control{
    		flex-grow: 1;
    		margin: 0 10px;
			width: 30px;
		}
		#current-time, #duration {
    		font-size: 16px;
		}
	</style>

HTML

<div class="audio-player">
    	<audio id="audio" src="gsmed.mp3"></audio>
    	<div class="controls">
        	<button id="play-pause">⏵</button>
        	<input type="range" id="seek-bar" value="0">
        	<span id="current-time">0:00</span> / <span id="duration">0:00</span>
			<input type="range" id="vol-control" min="0" max="1" step="0.1" value="0.5">
			<button id="mute">🔉</button>
			 <button id="download">⇩</button>
    	</div>
	</div>

JavaScript

<script>
	document.addEventListener('DOMContentLoaded', function() {
    	const audio = document.getElementById('audio');
    	const playPauseButton = document.getElementById('play-pause');
    	const seekBar = document.getElementById('seek-bar');
    	const currentTime = document.getElementById('current-time');
    	const duration = document.getElementById('duration');
		const muteButton = document.getElementById('mute');
		const downButton = document.getElementById('download');

    	playPauseButton.addEventListener('click', function() {
        	if (audio.paused) {
            	audio.play();
            	playPauseButton.textContent = '\u23F8';
        	} else {
            	audio.pause();
            	playPauseButton.textContent = '\u23F5';
        	}
    	});

    	audio.addEventListener('timeupdate', function() {
        	let value = (audio.currentTime / audio.duration) * 100;
        	seekBar.value = value;
        	currentTime.textContent = formatTime(audio.currentTime);
        	duration.textContent = formatTime(audio.duration);
    	});

    	seekBar.addEventListener('input', function() {
        	let time = (seekBar.value / 100) * audio.duration;
        	audio.currentTime = time;
    	});

    	function formatTime(seconds) {
        	var minutes = Math.floor(seconds / 60);
        	var seconds = Math.floor(seconds % 60);
        	if (seconds < 10) {
           	 seconds = '0' + seconds;
        	}
        	return minutes + ':' + seconds;
    	}

		const volumeControl = document.getElementById('vol-control');
			volumeControl.addEventListener('input', function() {
    		audio.volume = volumeControl.value;
		});

		muteButton.addEventListener('click', function() {
        	if (audio.muted) {
            	audio.muted = false;
            	muteButton.textContent = '\u{1F509}';
        	} else {
            	audio.muted = true;
            	muteButton.textContent = '\u{1F507}';
        	}
    	});

		downButton.addEventListener('click', function() {
        	url = audio.src;
			const a = document.createElement('a');
  			a.href = url;
  			a.download = url.split('/').pop();
  			document.body.appendChild(a);
  			a.click();
 			document.body.removeChild(a);
    	});
	});
</script>

Unicode and Other Characters

Most of the media symbols come from the Miscellaneous Technical Unicode page and UTF-8 code page 10. Unforunately there does not seem to be a consistant set of Unicode multimedia characters so you might be better off finding a set of icons or create your own.

To use the characters in HTML, use &#decimal; or &#xhex;. JavaScript is a little more complicated. It can only handle hexadecimal numbers up to FFFF using \uhex; after that then \u{hex} notation should be used.

Decimal Hex U+ Character Character Name
8659 U+21D3 Downwards double arrow
8681 U+21E9 Downwards white arrow
9193 U+23E9 Right-pointing double triangle
9194 U+223EA Left-pointing double triangle
9197 U+23ED Black right-pointing double triangle with vertical bar
9198 U+23EE Black left-pointing double triangle with vertical bar
9199 U+23EF Black right-pointing triangle with double vertical bar
9204 U+23F4 Black left-pointing triangle
9205 U+23F5 Black right-pointing triangle
9208 U+23F8 Black double vertical bar
9209 U+23F9 Black square for stop
9654 U+25B6 Black right-pointing triangle
9658 U+25BA Black right-pointing triangle
128256 U+1F500 🔀 Twisted rightwards arrows, shuffle
128257 U+1F501 🔁 Clockwise rightwards and leftwards open circle arrows, repeat
128263 U+1F507 🔇 Speaker with cancellation stroke
128264 U+1F508 🔈 Speaker, left speaker
128265 U+1F509 🔉 Speaker with one sound wave
128266 U+1F50A 🔊 Speaker with three sound waves

Of course, you do not need to use these characters, you can create your own button images.


Visualizations

I was hoping adding an audio visulizer to the audio tag would be easier than it turned out to be. The Web Audio API is comprehensive and built into nearly all browsers. Looking around, I found the audio-analyser on the MDM examples page on GitHub. After copying the file, it does not work as the browsers I tried it in say that "The ScriptProcessorNode is deprecated. Use AudioWorkletNode instead.". The trouble is, I cannot find a working example.

Unfortunately I am not experienced enough at using the API to create something that works.


Playlists

Nope

There is no easy way to add a playlist to an audio tag. For example, variations of:

<audio controls>
<source src="audio1.mp3" src="audio2.mp3.mp3" src="audio3.mp3.mp3">
</audio>

do not work. Just the first track is available to the audio instance. Neither does putting the tracks in an array and stepping through that, such as using, for example:

<audio id="play"controls>
</audio>

<srcipt>
const array= ["audio1.mp3", "audio2.mp3", "audio3.mp3"];
for (let i=0; i<array.length; i++) {
document.getElementById("play").src = array[i];
}
</srcipt>

What happens in this case is that the loop works, but only the final audio track in the array is available to the audio tag.

What Works

One of the best plain audio tag and JavaScript implementations is on the Hymns for Atheists site. The JavaScript for that can be found in the site's JS file.

The player works by adding an eventlistener to the audio tag and checking when the "ended" property changes from false to true.Much like this example on GitHub.

Some ideas

Adding a playlist to the HTML audio tag is fairly easy, all that is needed is a list of information about the various tracks and some way of navigating through it. One way is to use JSON (JavaScript Object Notation). In an array, the data can then be accessed sequentially or by index.

An example of this would be:

<script>
		// The track data
		const tracks = [{"url": "hallking.mp3", "title": "In the Hall of the Mountain King", "composer": "Edvard Grieg", "year": "1875"},
		{"url": "karelia.mp3", "title": "Karelia Suite", "composer": "Jean Sibelius", "year": "1893"},
		{"url": "ride.mp3", "title": "Ride of the Valkyries", "composer": "Richard Wagner", "year": "1854"}];

		// List the tracks 
		tracks.forEach(track => {
  			document.getElementById("tracklist").innerHTML = document.getElementById("tracklist").innerHTML + "

" + track.title + " written by " + track.composer + " in " + track.year + ".

";}) </script>

Buttons can be added and eventlisteners added to them so that things such as random play and looping can be implemented. In the following example autoplay is set to false, but it can be set to true.

🔀 🔁

Track List

Excerpt from:


Final Thoughts

Playing audio and video files on websites has never been easier but styling the player can still be awkward to match the theme of the page it is on. To provide a consistant look across browsers you could use one of the pre-built players or one of the many APIs.

The usual advice from years ago still applies, not everyone may like your choice of music and they could be listening to their own music while browsing your site so think carefully before starting your music as soon as your page loads.

Here's a little bit of trivia. JW Player was the original player used to stream YouTube videos.


Sources & Resources

<audio>: The Embed Audio element - MDN Web Docs
<video>: The Video Embed element - MDN Web Docs
Cross-browser audio basics - MDN Web Docs
How to style HTML5 audio tag - Stack Overflow
HTML DOM Audio Object - W3Schools
HTML DOM Video Object - W3Schools
HTML <audio> Tag - W3Schools
HTML <video> Tag - W3Schools
HTMLAudioElement - MDN Web Docs
HTMLMediaElement - MDN Web Docs
Hymns for Atheists - A good example of creating and using a playlist
List of Chromium and Non-Chromium Based Browsers - Computer City
Media control symbols - Wikipedia
Media technologies on the web - MDN Web Docs
Styling Your HTML5 Audio Player with Custom Controls - Machinet (Internet Archive)
Web APIs - MDN Web Docs
Web Audio API - MDN Web Docs
Webkit Pseudo Elements - GitHub

Character Code Pages and Icons

Alt Codes for Computer & User Interface Symbols - Alt Code Unicode
List of Arrows Symbols - Unicode Explorer
Material Icons - Material UI
Miscellaneous Symbols and Arrows - Unicode
Miscellaneous Technical - Unicode
Miscellaneous Technical - Unicode Explorer
Multimedia - Zappicon
Multimedia Vectors - SVG Repo
UTF-8 Geometric Shapes - W3Schools
UTF-8 Misc Technical - W3Schools