On Friday a reader of my last book wrote to me regarding a particular issue they were having in recreating a lesson from a chapter, particularly in IE 10. Rather than addressing just that issue, I thought I would expand upon the exercise significantly for an article here:

The idea is pretty simple: a giant red button as the sole element on a page, which animates and plays a sound when pressed. The markup is equally simple, using just a <button> element:

<button id="everybodydance" type="button">PARTY!</button>

The initial CSS is slightly more complex:

html { height: 100%; }
body { 
	min-height: 100%;
	margin: 0;
	display: flex;
	justify-content: center;
	align-items: center; 
}
button { 
	border-radius: 50%;
	width: 200px;
	height: 200px;
	border: none; 
	color: white;
	font-family: Avenir, Arial, sans-serif;
	font-weight: 900; font-size: 2.5rem;
	text-shadow: 0 3px 1px rgba(122,17,8,.8);
	box-shadow: 0 8px 0 rgb(183,9,0), 
		0 15px 20px rgba(0,0,0,.35);
	background: red;
	transition: .4s all ease-in;
	outline: none; cursor: pointer;
	text-align: center;
	-webkit-user-select: none;
	-webkit-tap-highlight-color: rgba(0,0,0,0);
}

The –webkit- prefixed code will prevent a copy prompt from appearing if the user has pressed a moment too long on it while using an iPhone, iPad, etc. (Note that you’ll also need vendor-prefixed codeand various values to achieve the flexbox layout in older browsers).

I’ll also add a pressed state, which will be added and removed with JavaScript:

.pressed { 
	padding-top: 3px;
	transform: translateY(4px);
	box-shadow: 0 4px 0 rgb(183,0,0),
		0 8px 6px rgba(0,0,0,.45);
}

I’ve excused removing the outline on the element – generally a no-no in – by the fact that the button will look (and sound!) significantly different when pressed.

Bring On The Noise

I’ve shown how to generate sound from UI elements using the HTML5 <audio> tag in past articles, but I decided to use the more complex Web Audio API for this example. I did so to gain several advantages, including far greater controlof audio and pre-buffering. The one problem with this approach is that it will of necessity eliminate sound in every version of Internet Explorer, which doesn’t yet support the API. (Edge, however, does). I’ll address how to deal with that and some other IE issues at the end of the article. For now, let’s look at writing support for everything else:


	var contextClass = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext);
var dance = document.getElementById("everybodydance");
if (contextClass) {
	var context = new contextClass();
} else {
	onError;
}
var request = new XMLHttpRequest();
request.open('GET', "gonna-make-you-sweat.mp3", true);
request.responseType = 'arraybuffer';
request.onload = function() {
	context.decodeAudioData(request.response, function(theBuffer) {
		buffer = theBuffer; }, onError);
		}
request.send();
function onError() { 
	console.log("Bad browser! No Web Audio API for you"); 
}
function unpress() { 
	dance.classList.remove("pressed"); 
}
function playSound() {
	dance.classList.add("pressed");
	var source = context.createBufferSource();
	source.buffer = buffer;
	source.connect(context.destination);
	source.start(0);
	var delay = 2000;
	setTimeout(unpress,delay);
}
dance.addEventListener('click', function(event) { 
	playSound(); 
});

I’m not going to explain the complexities of the Web Audio API here: that will be a series of articles in the future. I will, however, make three points:

  • as the API is getting the audio sample via an XMLHttpRequest, recreations of this code will have to be run from a server in order to work.
  • I only need the MP3 file, since Firefox now supports the codec.
  • I’m cheating slightly at the end, opting to simply “unpress” the button by using a set timeout rather than detecting the end of the sound sample playback. It’s totally possible (and safer) to sample the end of the sound, but since it is so short (and buffered before the user even clicks the button), using a timeout is justifiable.

Safeguarding IE & Older Browsers

As I’ve mentioned, Internet Explorer won’t play with this code nicely: partly due to its lack of support for Web Audio, partly because of its strange issues with cross-site resource requests. Dealing with IE could go one of several ways:

You could make button’s presentation dependent on WebAPI support, and provide different content to other browsers, by removing the button from your HTML and generating it completely via JavaScript, in the first conditional statement:

var contextClass = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext);
var dance = document.getElementById("everybodydance");
if (contextClass) {
	var context = new contextClass();
	var dance = document.createElement("button");
	dance.id = "everybodydance";
	dance.type = "button";
	dance.innerHTML = "PARTY!";
	var body = document.getElementsByTagName("body")[0];
	body.appendChild(dance);
} else {
	onError; /* JavaScript generated content for other browsers */
}

A complete version of this might also have some basic HTML code for the button, for those browsers that don’t have JavaScript at all.

Your issues with IE are not yet over: Internet Explorer will also move the text of the button slightly on click. This can be fixed (at least for IE 10+) by adding a <span> element around the button text: <button id="everybodydance" type="button"><span>PARTY!</span></button>

And adding the following CSS:

button span { 
	position: relative;
}

If you wanted to reduce the code to its most basic, without any audio, the script, added to the end of the page, would be:

var dance = document.getElementById("everybodydance");
function unpress() { 
	dance.classList.remove("pressed"); 
}
function playSound() {
	dance.classList.add("pressed");
	var delay = 2000;
	setTimeout(unpress,delay);
}
dance.addEventListener('click', function(event) { 
	playSound();
});

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/zqgGn