O'Reilly logo

HTML5 Canvas by Jeff Fulton, Steve Fulton

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Playing a Sound with No Audio Tag

Now that we have a sound playing in an HTML5 page and we are tracking the properties of the audio element on the canvas, it is time to step up their integration. The next step is to do away with the <audio> tag embedded in the HTML page.

If you recall from Chapter 6, we created a video element dynamically in the HTML page and then used the canPlayType() method of the HTMLVideoElement object to figure out what video file type to load for a particular browser. We will do something very similar for audio.

Dynamically Creating an Audio Element in JavaScript

The first step to dynamically creating audio elements is to create a global variable named audioElement. This variable will hold an instance of HTMLAudioElement that we will use in our canvas application. Recall that audio elements in an HTML page are instances of the HTMLAudioElement DOM object. We refer to them as audio objects when embedded in an HTML page, and as instances of HTMLAudioElement when created dynamically in JavaScript. However, they are essentially the same.

Note

Don’t fret if you don’t like using global variables. By the end of this chapter, we will show you a way to make these variables local to your canvas application.

Next, we create our event handler for the window load event named eventWindowLoaded(). Inside that function, we call the createElement() function of the DOM document object, passing the value audio as the type of element to create. This will dynamically create an audio object and put it into the DOM. By placing that object in the audioElement variable, we can then dynamically place it onto the HTML page with a call to the appendChild() method of the document.body DOM object:

window.addEventListener('load', eventWindowLoaded, false);
var audioElement;
function eventWindowLoaded() {
   audioElement = document.createElement("audio");
   document.body.appendChild(audioElement);

However, just having a dynamically created audio element is not enough. We also need to set the src attribute of the HTMLAudioElement object represented by audioElement to a valid audio file to load and play. But the problem is that we don’t yet know what type of audio file the current browser supports. We will get that information from a function we will create named supportedAudioFormat(). We will define this function so that it returns a string value representing the extension of the file type we want to load. When we have that extension, we concatenate it with the name of the sound we want to load, and set the src with a call to the setAttribute() method of the HTMLAudioElement object:

var audioType = supportedAudioFormat(audioElement);

If a valid extension from supportedAudioFormat() is not returned, something has gone wrong and we need to halt execution. To handle this condition in a simple way we create an alert() message and then return from the function, effectively halting execution. While this is not a very robust form of error handling, it will work for the sake of this example:

if (audioType == "") {
   alert("no audio support");
   return;
}
audioElement.setAttribute("src", "song1." + audioType);

Finally, like we did with video, we will listen for the canplaythrough event of audioElement so that we know when the sound is ready to play:

audioElement.addEventListener("canplaythrough",audioLoaded,false);

Finding the Supported Audio Format

Before the code in the previous section will work, we need to define the supportedAudioFormat() function. Since we are adding audio objects dynamically to the HTML page, we do not have a way to define multiple <source> tags like we can in HTML. Instead, we are going to use the canPlayType() method of the audio object to tell us which type of audio file to load. We already introduced you to the canPlayType() method in Chapter 6, but to refresh your memory, canPlayType() takes a single parameter—a MIME type. It returns a text string of maybe, probably, or “” (nothing). We are going to use these values to figure out which media type to load and play. Just like in Chapter 6, and for the sake of this exercise, we are going to assume that both maybe and probably equate to yes. If we encounter either result with any of our three MIME types (audio/ogg, audio/wav, audio/mp3), we will return the extension associated with that MIME type so the sound file can be loaded.

Note

The next function is essentially the same as the one we created in Chapter 6 to handle video formats. The obvious changes here are with the MIME types for audio.

In the function below, audio represents the instance of HTMLAudioElement that we will test. The returnExtension variable represents that valid extension for the first MIME type found that has the value of maybe or probably returned:

function supportedAudioFormat(audio) {
   var returnExtension = "";
   if (audio.canPlayType("audio/ogg") =="probably" || 
       audio.canPlayType("audio/ogg") == "maybe") {
         returnExtension = "ogg";   } else if(audio.canPlayType("audio/wav") =="probably" || 
       audio.canPlayType("audio/wav") == "maybe") {
         returnExtension = "wav";
   } else if(audio.canPlayType("audio/mp3") == "probably" || 
       audio.canPlayType("audio/mp3") == "maybe") {
         returnExtension = "mp3";
   }

   return returnExtension;

}

Notice that we do not check for a condition when no valid audio format is found and the return value is “”. If that is the case, the code that has called this function might need to be written in a way to catch that condition and alter the program execution. We did that with the test of the return value and the alert() message, which we described in the previous section.

Note

If you want to test the error condition with no valid return value from this function, simply add an extra character to the MIME type (e.g., audio/oggx) to make sure an empty string is always returned.

Alternatively, you can use Modernizr to test for audio support. If you have included the Modernizr JavaScript library in your HTML page (as we have done in every application we have written thus far), you can access the static values of Modernizr.audio.ogg, Modernizr.audio.wav, and Modernizr.audio.mp3 to test to see whether those types are valid. These are not Booleans—they evaluate to the same probably, maybe, and “” values that we get from a call to canPlayType(). If you are comfortable using Modernizr for all your tests, you can replace the test in the code with tests of these Modernizr static values.

Playing the Sound

Finally, we get to the point where we can play a sound inside our canvasApp() function. Since we preloaded the sound originally outside the context of this function into a variable with a global scope, we just need to call the play() function audioElement to start playing the sound:

audioElement.play();

Figure 7-4 shows what this canvas application will look like when executed in an HTML5-compliant web browser (notice that we have left the display of the audio properties in this application).

Sound loaded and played “on” the canvas

Figure 7-4. Sound loaded and played “on” the canvas

Look Ma, No Tag!

Now, check out the full application in Example 7-4. Notice that there is no <audio> tag defined in the HTML, but the sound still plays. This is our first step toward integrating HTMLAudioElement objects with HTML5 Canvas.

Example 7-4. Playing a sound with no tag

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH7EX4: Playing A Sound With No Tag</title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
var audioElement;
function eventWindowLoaded() {

   audioElement = document.createElement("audio");
   document.body.appendChild(audioElement);
   var audioType = supportedAudioFormat(audioElement);
   if (audioType == "") {
      alert("no audio support");
      return;
   }
   audioElement.setAttribute("src", "song1." + audioType);
   audioElement.addEventListener("canplaythrough",audioLoaded,false);

}

function supportedAudioFormat(audio) {
   var returnExtension = "";
   if (audio.canPlayType("audio/ogg") =="probably" || 
       audio.canPlayType("audio/ogg") == "maybe") {
         returnExtension = "ogg";
   } else if(audio.canPlayType("audio/wav") =="probably" || 
       audio.canPlayType("audio/wav") == "maybe") {
         returnExtension = "wav";
   } else if(audio.canPlayType("audio/mp3") == "probably" || 
       audio.canPlayType("audio/mp3") == "maybe") {
         returnExtension = "mp3";
   }

   return returnExtension;

}

function canvasSupport () {
     return Modernizr.canvas;
}


function audioLoaded(event) {

   canvasApp();

}

function canvasApp() {

  if (!canvasSupport()) {
          return;
        }

  function  drawScreen () {

      //Background
      context.fillStyle = '#ffffaa';
      context.fillRect(0, 0, theCanvas.width, theCanvas.height);

      //Box
      context.strokeStyle = '#000000';
      context.strokeRect(5,  5, theCanvas.width−10, theCanvas.height−10);

      // Text
      context.fillStyle = "#000000";
      context.fillText  ("Duration:" + audioElement.duration,  20 ,20);
      context.fillText  ("Current time:" + audioElement.currentTime,  20 ,40);
      context.fillText  ("Loop: " + audioElement.loop,  20 ,60);
      context.fillText  ("Autoplay: " +audioElement.autoplay,  20 ,80);
      context.fillText  ("Muted: " + audioElement.muted,  20 ,100);
      context.fillText  ("Controls: " + audioElement.controls,  20 ,120);
      context.fillText  ("Volume: " + audioElement.volume,  20 ,140);
      context.fillText  ("Paused: " + audioElement.paused,  20 ,160);
      context.fillText  ("Ended: " + audioElement.ended,  20 ,180);
      context.fillText  ("Source: " + audioElement.currentSrc,  20 ,200);
      context.fillText  ("Can Play OGG: " + audioElement.canPlayType("audio/ogg"),  
                        20 ,220);
      context.fillText  ("Can Play WAV: " + audioElement.canPlayType("audio/wav"),  
                        20 ,240);
      context.fillText  ("Can Play MP3: " + audioElement.canPlayType("audio/mp3"),  
                        20 ,260);

   }

   var theCanvas = document.getElementById("canvasOne");
   var context = theCanvas.getContext("2d");
   audioElement.play()

   setInterval(drawScreen, 33);

}

</script>

</head>
<body>
<div style="position: absolute; top: 50px; left: 50px;">

<canvas id="canvasOne" width="500" height="300">
 Your browser does not support HTML5 Canvas.
</canvas>
</div>
</body>
</html>

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required