Play Sound on :hover
Published by Chris Coyier
When you google around for how to play a sound with CSS, you'll find:
- Some stuff about Counter Strike: Source
- Some stuff about
play-duringandcue-beforeand stuff that looks promising but really it's for aural stylesheets (accessibility / screen reader stuff) not just how to make donkey grunts when you roll over a menu item in any ol' browser
I'd argue that sounds are part of design and thus the ability to play/trigger it belongs in CSS, but alas, we're not there yet. To play sounds when the mouse goes over a certain area, we're going to need to rely on HTML5 or Flash. But nobody around here wants to deal with Flash right? So let's do it with HTML5, which can play sound through its <audio> element (Firefox 3.5+, Chrome 3+, Opera 10.5+, Safari 4+, IE 9+). To get as much browser support as we can, we'll do it like this with both an MP3 source (WebKit and IE) and an OGG source (Firefox and Opera).
<audio>
<source src="audio/beep.mp3"></source>
<source src="audio/beep.ogg"></source>
Your browser isn't invited for super fun audio time.
</audio>
If you insert the code exactly as above into a page, you won't see or hear anything. If you want a little player element, make sure to use the controls attribute (<audio controls>). If you want it to play but not be seen, make sure to use the autoplay element (<audio autoplay>). Or both...
Our goal is to have the sound play when the mouse hovers over a certain element, like a menu item. Again unfortunately, we can't tell an <audio> element what to do through CSS, so we'll need JavaScript. To play the sound with JavaScript:
var audio = document.getElementsByTagName("audio")[0];
audio.play();
// or with an ID
var audio = document.getElementById("mySoundClip");
audio.play();
Let's use jQuery, just because it's going to make selecting and dealing with events easier.
var audio = $("#mySoundClip")[0];
audio.play();
So to make this sound begin to play when the mouse hovers over a certain element:
var audio = $("#mySoundClip")[0];
$("nav a").mouseenter(function() {
audio.play();
});
Another way...
The teaser page for the Goodfoot mobile app uses a similar technique to play weird groaning noises (via Dave Rupert) when you hover over the yeti dude. They do it by injecting a new audio element into the DOM everytime that yeti dude is hovered:
$("#speak").mouseenter(function(){
$("<audio></audio>").attr({
'src':'audio/'+Math.ceil(Math.random() * 5)+'.mp3',
'volume':0.4,
'autoplay':'autoplay'
}).appendTo("body");
});
That could probably be improved a little, to support OGG as well. Not sure what the volume attribute is all about, that's not spec nor supported anywhere I've seen. This is a modified from Jeffrey Way:
function addSource(elem, path) {
$('<source>').attr('src', path).appendTo(elem);
}
$("#speak").mouseenter(function(){
var audio = $('<audio />', {
autoPlay : 'autoplay'
});
addSource(audio, 'audio/'+Math.ceil(Math.random() * 5)+'.mp3');
addSource(audio, 'audio/'+Math.ceil(Math.random() * 5)+'.ogg');
audio.appendTo('body');
});
I'm totally fine with this approach, since it seems to work fine and that's the bottom line. After the sound has been played once it seems to cache and play quickly, which is good.
Another way to handle it would be to put all three audio elements onto the page right away.
<audio preload="auto" id="sound-1" > ... src & fallback ... </audio>
<audio preload="auto" id="sound-2" > ... src & fallback ... </audio>
<audio preload="auto" id="sound-3" > ... src & fallback ... </audio>
Then randomly choose one to play:
$("#speak").mouseenter(function() {
$("#sound-" + Math.ceil(Math.random() * 3))[0].play();
});
Trials and Troubles: Overlapping Sounds
My original idea for playing with this was a navigation menu that played a little clicking sound while hovering over them. Immediately this uncovered a problem: you can hover over menu items triggering a .play() a lot faster than that sound can finish playing. A single audio element can't play it's own sound in an overlapping way. It just ignores further requests to .play() until it's completed.
My first try was to use .pause() to stop the playing and then .play() again, but in my testing that doesn't help much. It seems to honor the pause but then not the play in many cases.
The smoothest way I could find to do it was to duplicate the audio element for each menu item. That way each menu item has it's own audio clip to play and the sounds can overlap
$("nav a") // loop each menu item
.each(function(i) {
if (i != 0) { // only clone if more than one needed
$("#beep")
.clone()
.attr("id", "beep-" + i)
.appendTo($(this).parent());
}
$(this).data("beeper", i); // save reference
})
.mouseenter(function() {
$("#beep-" + $(this).data("beeper"))[0].play();
});
$("#beep").attr("id", "beep-0"); // get first one into naming convention
I love alternatives to using Flash and I’ll have to use this one in the future. Thanks Chris!
Ok I dig it! Cool tutorial, but aren’t sounds the most annoying thing on websites? I mean when people are browsing at work then suddenly music starts playing, or everytime you roll over a navigation you hear that annoying “click” “click” “click”.
I’m just saying. Good tutorial though ;)
Yeah, I agree. I would accept it if it was a very very quiet “click” like if you went to the last part of the demo and turn the volume down to where theres about two millimeters of space away from the bottom
Chris, when why click the demo button, my antivirus says that it is “HTML script infection” and aborts connection.
Nice article, though.
Thanks for the tutorial, but this makes me shutter thinking of people actually adding this to their site’s navigation buttons. 2advanced-era bleepity-boopity websites all over again…
Haha! “weird groaning noises”
People, welcome to annoying (non-)Flash websites 2.0
If you really have to, I think the best way to go is to create an audio element and remove it when the sound ends, this way:
• you don’t have to wait for the previous one to end
• the previous one will not be interrupted
Consider preloading the sound as well
Check out Scott Schiller’s Sound Manager 2. It’s an excellent, excellent, library. It’s used by Last.FM when the user clicks the play button next to a song that’s in an unordered list (as opposed to the main player). Schiller uses it on his own website to do exactly what you’re describing (with the hover effect) here @ http://www.schillmania.com/
I believe the latest version will even utilize HTML5 as needed.
If the goal is to drive users away from a website, annoying little sounds on navigation should be very effective.
It’s an interesting proof of concept but I shudder to think where it will lead.
Folks —
Totally get where you are coming from on the “Eww gross” angle.
Sounds can be misused.
But.
Color can be misused. Images can be misused. Fonts can be misused. Words can be misused. Paint can be misused. Hammers and Nails can be misused.
This is just a tech demo which gives us the power to be creative. Use wisely.
Your completely correct – in the right environment this would be completely acceptable. Think about sites targeted toward children or just on buttons on something like Pandora or Last.FM which is sound oriented.
I said completely twice in the first sentence and would really love to edit it… :)
Last example of JavaScript code is the best solution, I can now hear many active hovers per one second. Thanks Chris!
I immediately thought of my old college instructor that despised Flash and all weird clicks and sounds on webpages unless it’s a site for music or video. I wonder what he would think of this. It’s very innovative to say the least.
Oh god, I thought these annoying flash-hover sounds were a thing of the past. I agree, knowledge is good, but I really hope this doesn’t start trending with html sites..
This is an amazing tut!! i saw lot of arguments goin on out here but whatever it is, it is a good thing that we can have sounds on hover without using flash!!
ooh! how I love it though!! :D
I read your article a few hours ago already, now I just came back to check on the comments. And I have to say that I’m very happy about the reactions you are gettting ;)
That is great post. It’s very nice. Thanks for shearing your ideas.
Nice idea! I do wonder if there’s a way to do this without JS. One thought that comes to mind is using CSS :before or :after selectors. Perhaps something like
.navSelect:hover:after {
content: "
Your browser isn't invited for super fun audio time.
"
}
I’m not sure if you can stack pseudoclasses, so it may have to be something like .navSelect:hover span:after or a:after.
Wouldn’t this get the desired result and technically be controlled by CSS?
sorry, post got eaten… Here’s what it should have been:
.navSelect:hover:after {
content: "
<audio autoplay>
<source src="audio/beep.mp3"></source>
<source src="audio/beep.ogg"></source>
Your browser isn't invited for super fun audio time.
</audio>
Your browser isn't invited for super fun audio time.
"
}
I’m afraid that doesn’t work. What you put in the content property is treated like a string, last time I tried html elements were not created that way.
Adding my two cents – just be very, very careful where you use this. So far I can’t imagine any circumstances where it would be appropriate, but there may be some.
Could you queue the sound events by adding them to the fxqueue?
Also, you might want to add the id to the first html block as it’s used for the rest of the example.
Great read, I was just working on a website that requires accessibility as its number one priority. Unfortunately aural stylesheets are deprecated and CSS 3 speech modules are still in the working draft. However I did find the aural box model to be interesting. You can find it here
even I prefer to use flash this tutorial might be helpfull for me,
thanks
I just try your demo, so COOL!
Something like flash, but faster loading time for the page, need not to wait from 20% to 100%.
I think that it is more suitable for blogs, or personal website, not too good for online shop.
I will try, thank you.
“Play Sound on :hover”?
Wouldn’t “Play sound using javascript” be a better title for this article since that’s what you’re really doing? It’s not as amazing as playing using :hover, but the title is a rather misleading…
Also, sounds on web pages are pure evil^^ (not counting music & video players).
It is better call stop() first before new play() to prevent silent buttons. Also there is way using jQuery to add sound dynamicaly and dynamicaly delete them from dom as they are played. So It can play more sounds at same time.
This is a great tutorial well distributed and explained, having sounds on a hover state can be annoying, but this tutorial is just to explain that sound can be applied without the use of flash.
HTML 5 has loads of potential to do great things and definitely one to look out for the future.
how about long audio?
can we stop it when the mouse out of the hover area?
Thanks for this, it was just what I needed :D
Does anyone know if there any way of changing the control buttons using CSS?
Now there is just no argument left for the use of flash, animation and sound wit css :-)
This is really cool. However, if abused it could quickly become very annoying! HTML5 looks really good, although I’m not sure Flash is going to be dropped for quite some time yet!
I think Flash will be dropped very soon. I don’t think it will ever be eliminated as a medium for video (among other features), but as HTML5 moves up as a force for video, Flash will loose it’s tentative foothold.
I don’t know any designers who care about web standards and semantics that still employ flash in their (critical) arsenal. I think it will always have a place, but will supplement and enhance websites rather than be critical to it’s integrity.
/rant
Interesting stuff! Thanks!
BTW this [0] doesn’t make any sense:
var audio = $(“#mySoundClip”)[0];
The alternative would be:
var audio = document.getElementById(“mySoundClip”);
For what it’s worth, you’d probably want to cache the result of the Math.random call, otherwise there’s a chance that the sound files will be completely different.
Not that it matters for this demo anyway :)