treehouse : what would you like to learn today?
Web Design Web Development iOS Development

Play Sound on :hover

Published by Chris Coyier

When you google around for how to play a sound with CSS, you'll find:

  1. Some stuff about Counter Strike: Source
  2. Some stuff about play-during and cue-before and 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

View Demo   Download Files

Related

View Comments

Comments

  1. I love alternatives to using Flash and I’ll have to use this one in the future. Thanks Chris!

  2. 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

  3. Chris, when why click the demo button, my antivirus says that it is “HTML script infection” and aborts connection.

    Nice article, though.

  4. Tom Silva
    Permalink to comment#

    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…

  5. Haha! “weird groaning noises”

  6. shitwizard
    Permalink to comment#

    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.

  7. 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.

  8. 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… :)

  9. Last example of JavaScript code is the best solution, I can now hear many active hovers per one second. Thanks Chris!

  10. 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.

  11. 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..

  12. 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

  13. Permalink to comment#

    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 ;)

  14. That is great post. It’s very nice. Thanks for shearing your ideas.

  15. lastlifelost
    Permalink to comment#

    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?

    • lastlifelost
      Permalink to comment#

      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.

  16. David Smith
    Permalink to comment#

    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.

  17. Permalink to comment#

    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.

  18. Permalink to comment#

    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

  19. Permalink to comment#

    even I prefer to use flash this tutorial might be helpfull for me,
    thanks

  20. 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.

  21. Hugo
    Permalink to comment#

    “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).

  22. Lweek
    Permalink to comment#

    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.

  23. 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.

  24. Permalink to comment#

    how about long audio?
    can we stop it when the mouse out of the hover area?

  25. Thanks for this, it was just what I needed :D
    Does anyone know if there any way of changing the control buttons using CSS?

  26. Permalink to comment#

    Now there is just no argument left for the use of flash, animation and sound wit css :-)

  27. Permalink to comment#

    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!

  28. 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

  29. Permalink to comment#

    Interesting stuff! Thanks!

    BTW this [0] doesn’t make any sense:
    var audio = $(“#mySoundClip”)[0];

  30. 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 :)

This comment thread is closed. If you have important information to share, you can always contact me.