Change an Image AND Play a Sound on Mouse-over

For an actress friend of mine I wanted the mouse-over effect as well as a sound to play. She does many characters, and I figured it'd be fun to have each section of her web page represented by a character, who's image changes and says something related as you mouse-over. Now, I managed to do this in Java, but it takes way too long to load (though you can get this cocktail-party sounding thing if you mouse over all of the images really fast). I've come up with a blended solution of JavaScript and Java (doing only the sound part) that seems to work pretty well, with only a slight delay at page load. The code is very similar to the image change on mouse-over above, with the additional sound support. And you can still get the cocktail party effect!

The Java code is:


          import java.applet.Applet;

          import java.applet.AudioClip;

          import java.awt.HeadlessException;

          import java.net.URL;

          import java.util.ArrayList;



          /*

           * Define a Java class that collects sounds and plays them by

           * request.  Sounds are stored in a 0-based ArrayList, and

            * retrived using the index of the store order.  Note that a

           * null can be passed in as a sound name, which has the simple

           * effect of playing no sound when requested.

           */

          public class PlaySounds extends Applet

          {

              // Define the sound list to be maintained for the

              // life of the page.

              private ArrayList sounds = new ArrayList();

              

              /**

               * The default constructor - not much happening here.

               * @throws java.awt.HeadlessException

               */

              public PlaySounds() throws HeadlessException

              {

                  super();

              }

              

              /*

               * Play a sound based on the passed index.  We assume the

               * passed index is valid, since this is set up by the

               * associated JavaScript code - no hand coding should be

               * able to screw this up.

               */

              public void PlaySoundByIndex( int index )

              {

                  // The stored sound may be null to simply hold the index's

                  // place, in case no sound is associated with the entry.

                  if ( null != sounds.get( index ) )

                  {

                      // We know we stored an audio clip.  Just play it.

                      ( (AudioClip) sounds.get( index ) ).play( );

                  }

              }



              /*

               * Load a sound for playing later.  Sounds are loaded and stored

               * in the 0-based array sounds in the order in which they are

               * passed to this routine.  It is up to the caller to keep track

               * of what sound has what index (easily done by the JavaScript code

               * that comes with this class).  Note that a null can be passed to

                * this routine instead of a string to save an array slot for no

               * sound.

               */

              public void LoadSound( String sound )

              {

                  // Is this a place-holder for no sound?

                  if ( null == sound )

                  {

                      // Save no sound.

                      sounds.add( null );

                  }

                  else

                  {

                      // Got a string.

                      URL soundURL;

                      try

                      {

                          // Convert the string to a full URL

                          soundURL = new URL( getDocumentBase( ), sound );

                      }

                      catch( Exception e )

                      {

                          // Report any exceptions to the Java console.

                          System.out.println( e.toString() );



                          // And save a spot for the bad sound.

                          sounds.add( null );

                          return;

                      }



                      // Generate an audio clip from the URL.

                      AudioClip a = getAudioClip( soundURL );

                      if ( null == a )

                      {

                          // URL was not valid - report it.

                          System.out.println( "No sound " + soundURL.toString() );



                          // And save a spot for the bad sound.

                          sounds.add( null );

                      }

                      else

                      {

                          // Add the sound.

                          sounds.add( a );

                      }

                  } // End of if we're saving a sound

              }

          }

       

Of course, if you just want to download the class file, it's here

Like I said, the javascript code is very similar to the previous code, save for the sound handling. Due to the initial load delay in this version, I'm keeping both on this page. But, if you want to do ANY mouse-overs with sound, use only this version of the code. You just don't specify a sound parameter.

To handle the sound stuff, the first call to MouseoverImageWithSound() inserts the code to load the PlaySounds applet. It also handles loading the sound, so all of the downloading happens at page load time. I keep mentioning the delay, but it's really pretty fast, especially compared to the all-Java solution I used to have. I think alot of it has to do with the fact that the Java applet is only loaded once for all instances, rather than for each instance as I had it before.

Here's the JavaScipt. Same ol' comment applies about using server-side includes.


          <head>

             <script type="text/javascript">

                <!--

                   // This prefix is used to uniquely identify the button images.

                   // This name should not be used in other code on the page.  It

                   // is defined here so it can be easily modified.

                   obscureNamePrefix = "MouseoverImage";



                   // As new mouse-over buttons are introduced, they are loaded in these

                   // arrays, and indexed in the order seen.

                   buttonNames = new Array( );

                   mouseOverImages = new Array( );

                   mouseOutImages = new Array( );



                   // Keep track as to whether the applet loading HTML was added to

                   // the page.

                   addedApplet = 0;



                   function ChangeImageWithSound( buttonIndex )

                   {

                      // Change the button's image to the alternate image.

                      eval( "document." + buttonNames[ buttonIndex ] + ".src = mouseOverImages[ buttonIndex ].src;" );



                      // Play the sound

                      document.PlaySounds.PlaySoundByIndex( buttonIndex );

                   }



                    function ResetImageWithSound( buttonIndex )

                   {

                      // Reset the button's image to the default image.

                      eval( "document." + buttonNames[ buttonIndex ] + ".src = mouseOutImages[ buttonIndex ].src;" );

                   }



                   function MouseoverImageWithSound( link, startImage, mouseOverImage, sound )

                   {

                      // Add the applet for sounds on the first MouseoverImageWithSound()

                      if ( 0 == addedApplet )

                      {

                         appletString = "<applet code=\"PlaySounds.class\" name=\"PlaySounds\" width=0 height=0></applet>";

                         document.writeln( appletString );

                         ++addedApplet;

                      }



                      // What index are we working on?

                      index = buttonNames.length;



                      // Define the button name to be the obscure name and the button index.

                      buttonName = obscureNamePrefix + index;



                      // Save the button name for later use.

                      buttonNames.push( buttonName );



                      // Save the mouse-over image for use.

                      moImage = new Image();

                      moImage.src = mouseOverImage;

                      mouseOverImages.push( moImage );



                      // Save the original image for use.

                      sImage = new Image();

                      sImage.src = startImage;

                      mouseOutImages.push( sImage );



                      // Save the sound for use.  Note this may be null.

                      document.PlaySounds.LoadSound( sound );



                      // Create the HTML that invokes the changing images for this index.

                      string = new String();

                      string = "<a href=\"" + link + "\"";

                      string += " onmouseOver=\"ChangeImageWithSound( " +

                                index + " )\"";

                      string += " onmouseOut=\"ResetImageWithSound( " +

                                index + " )\">";

                      string += "<img src=\"" + startImage + "\" name=\"" +

                                 buttonName + "\" ></a>";



                      // Write the code into the document.

                      document.write( string );

                   }

                -->

             </script>

          </head>

       

Now, to use this code, place a call to MouseoverImageWithSound() in your HTML:


          <script type="text/javascript">

             <!--

                MouseoverImageWithSound( "index.shtml", "Graphics/StartImage.gif", "Graphics/MouseoverImage.gif", "Media/Excellent.wav" );

             -->

          </script>

       

This call will be replaced by the HTML needed to create a link to the page you specified in the first parameter, with the default image as the second parameter. As the user moves their mouse over the image, it'll change to the image listed as the third parameter and play the sound listed in the fourth (optional) parameter. Like this:


This page is maintained by Jim Campanell. Please send comments to