Programming Simple Games Using Canvas or SVG

This section compares how to use Canvas and SVG to create a simple arcade game that can run in a webpage.

  • Canvas Code Sample Discussion
    • Body Code
    • Script Code
  • Global Variables
  • drawGameCanvas Function
  • drawBall Function
  • whatKey Function
  • SVG Code Sample
  • SVG Code Sample Discussion
  • Body Code
  • Script Code
    • Global Variables
    • drawGameSVG Function
  • drawBall Function
  • whatKey Function
  • Comparison Summary
  • Related topics

This topic contains two annotated stand-alone code samples that demonstrate a generic racquetball game. The object of the game is to hit a ball with a paddle so it bounces off the top and sides of the court. These samples are written in HTML5 and JavaScript. They explain the basic principles of drawing the court, ball, and paddle, creating the action, detecting collisions, and ending the game. The first code sample presents the game in Canvas and shows how Canvas uses pixels in immediate mode to draw the board and paddle with rectangles and the ball with an arc. Next, the code describes how to color the board, paddle, and ball. The second sample is an SVG version that shows how SVG uses shapes in retained mode to display a circle for the ball, and rectangles for the board and paddle, with appropriate colors added. The technical discussion after each sample explains how the code works. The final comparison material summarizes the main differences in creating a racquetball game program that can be inserted in a web page.

Canvas Code Sample

<!DOCTYPE html>
<html>
  
  <head>
    <script type="text/javascript">
      // Global variables.
      var ballX = 150; // Ball x position.
      var ballY = 150; // Ball y position.
      var ballDX = 2; // Change in ball x position.
      var ballDY = 4; // Change in ball y position.
      var boardX = 300; // Board width.
      var boardY = 300; // Board height.
      var paddleX = 150; // Initial paddle location.
      var paddleH = 10; // Paddle height.
      var paddleD = boardY - paddleH; // Paddle depth.
      var paddleW = 150; // Paddle width.
      var canvas; // Canvas element.
      var ctx; // Canvas context.
      var gameLoop; // Game loop time interval.
      // This function is called on page load.


      function drawGameCanvas() {

        // Get the canvas element.
        canvas = document.getElementById("gameBoard");

        // Make sure you got it.
        if (canvas.getContext) {
          // Specify 2d canvas type.
          ctx = canvas.getContext("2d");

          // Play the game until the ball stops.
          gameLoop = setInterval(drawBall, 16);

          // Add keyboard listener.
          window.addEventListener('keydown', whatKey, true);

        }
      }

      function drawBall() {

        // Clear the board.
        ctx.clearRect(0, 0, boardX, boardY);

        // Fill the board.
        ctx.fillStyle = "thistle";
        ctx.beginPath();
        ctx.rect(0, 0, boardX, boardY);
        ctx.closePath();
        ctx.fill();

        // Draw a ball.
        ctx.fillStyle = "tomato";
        ctx.beginPath();
        ctx.arc(ballX, ballY, 15, 0, Math.PI * 2, true);
        ctx.closePath();
        ctx.fill();

        // Draw the paddle.
        ctx.fillStyle = "navy";
        ctx.beginPath();
        ctx.rect(paddleX, paddleD, paddleW, paddleH);
        ctx.closePath();
        ctx.fill();

        // Change the ball location.
        ballX += ballDX;
        ballY += ballDY;

        // Bounce on a left or right edge.
        if (ballX + ballDX > boardX - 15 || ballX + ballDX < 15) ballDX = -ballDX;

        // If ball hits the top, bounce it. 
        if (ballY + ballDY < 15) ballDY = -ballDY;
        // If the ball hits the bottom, check see if it hits a paddle.
        else if (ballY + ballDY > boardY - 15) {
          // If the ball hits the paddle, bounce it.
          if (ballX > paddleX && ballX < paddleX + paddleW) ballDY = -ballDY;
          // Otherwise the game is over.
          else {
            clearInterval(gameLoop);
            alert("Game over!");
          }
        }
      }

      // Get key press.


      function whatKey(evt) {

        switch (evt.keyCode) {
          // Left arrow.
        case 37:
          paddleX = paddleX - 20;
          if (paddleX < 0) paddleX = 0;
          break;

          // Right arrow.
        case 39:
          paddleX = paddleX + 20;
          if (paddleX > boardX - paddleW) paddleX = boardX - paddleW;
          break;
        }
      }
    </script>
  </head>
  
  <body onload="drawGameCanvas()">
    <h1>
      Canvas Racquetball
    </h1>
    <div>
      <canvas id="gameBoard" width="300" height="300">
      </canvas>
    </div>
  </body>

</html>

Canvas Code Sample Discussion

This Canvas code sample uses a standard HTML5 header: <!doctype html>, so that browsers can distinguish it as part of the HTML5 specification.

This code is divided into two major parts:

  • Body Code
  • Script Code

Body Code

The drawGameCanvas function is called when the page is loaded. The canvas tag is part of the body and the initial width and height is specified, as is the ID attribute. The ID is required so that the canvas element can be added to the object model of the page.

Script Code

The script code has a set of global variables and three functions: drawGameCanvas, drawBall, and whatKey. The global variables are initialized when the page is loaded and so is drawGameCanvas. drawBall is called near the end of drawGameCanvas and is attached to a timer event called gameLoop. The drawBall function is called every 16 milliseconds. The third function, whatKey, is attached to an event listener and "listens" for the “keydown” event, which is triggered every time a key is pressed.

Global Variables

This section initializes several variables that are used throughout the game.

  • ballX and ballY define the initial ball position at the center of the board (150,150).
  • ballDX and ballDy initialize the intial rate of change for the ball's position.
  • boardX and boardY initialize the height and width of the board (300 x 300).
  • paddleX defines the initial x posiition (top left x) of the paddle (150).
  • paddleH defines the absolute height of the paddle (10).
  • paddleD is the initial y position (top left y) of the paddle (290).
  • paddleW is the width of the paddle (150).
  • canvas, ctx, and gameLoop are created as global variables so they can be accessed from anywhere in the script code.

drawGameCanvas Function

This function is called on page load. It gets the canvas by using the ID of the canvas element in the body code. It then gets the context of the canvas, making it ready to accept drawing.

Then the game loop is set up with a setInterval call to the drawBall function. This function is be called every 16 milliseoncs. Most games have a game loop that is called at set intevals to perform the animations.

Finally the whatKey keyboard event listener is set up to process any key events.

drawBall Function

This function is called by an event timer every 16 millisecods. The following tasks are performed every time this function is called:

  • The game board is cleared and filled. clearRect is called to erase the canvas and then a color (thistle) is specified. A rectangle path is defined and filled.
  • The ball is drawn at its current position specifying fillStyle, beginPath, arc, closePath, and fill. The position of the ball is defined by ballX and ballY, which will change based on collisions with the walls and paddle.
  • The paddle is then drawn in the same manner, but with the specifications of paddleX, paddleD, paddleW, and paddleH.
  • The location of the ball is changed for the next time it will be drawn, based on the changes of ballDX and ballDY. This assumes that the ball is not hitting an edge or paddle.
  • If the ball hits the left or right edge, the ball ballDX value is reversed, which will make it bounce in the opposite direction.
  • If the ball hits the top edge, the ball ballDY value is reversed, which will make it bounce in the opposite direction.
  • If the ball hits the bottom, a check is made to see if it hit the paddle. If it did, then the ball ballDY is reversed, making it bounce. However, if the ball hit the bottom but not the paddle, the game is over.
  • If the game is over, the gameLoop is cleared (stopping the ball from moving) and an alert is displayed, announcing that the game is over.

whatKey Function

The whatKey function sets up the event listener so that is listens for a keydown event using a switch statement. It does this by looking for two keycodes.

If the left arrow is pressed, the keycode 37 is recognized and the paddle's paddleX value is decremented by 20 pixels. However, if the left edge of the paddle is less than zero, the paddle will not move because the paddle value will be set to 0 (zero).

If the right arrow is pressed, the keycode 39 is recognized and the paddle's paddleX value is incremented by 20 pixels. However, if the right edge of the paddle is greater than the width of the board, the paddle will not move because the paddle value will be set to the value of the right edge of the board.

SVG Code Sample

<!DOCTYPE HTML>
<html>
  
  <head>
    <script type="text/javascript">
      // Global variables
      var ballX = 150; // Ball x position.
      var ballY = 150; // Ball y position.
      var ballDX = 2; // Change in ball x position.
      var ballDY = 4; // Change in ball y position.
      var boardX = 300; // Board width.
      var boardY = 300; // Board height.
      var paddleX = 150; // Initial paddle location.
      var paddleH = 10; // Paddle height.
      var paddleD = boardY - paddleH; // Paddle depth.
      var paddleW = 150; // Paddle width.

      // This function is called on page load.


      function drawGameSVG() {

        // Play the game until the ball stops.
        gameLoop = setInterval(drawBall, 16);

        // Add keyboard listener.
        window.addEventListener('keydown', whatKey, true);
      }

      function drawBall() {


        // Change the ball location.
        ballX += ballDX;
        ballY += ballDY;
        ball.setAttribute("cx", ballX);
        ball.setAttribute("cy", ballY);


        // Bounce on a left or right edge.
        if (ballX + ballDX > boardX - 15 || ballX + ballDX < 15) ballDX = -ballDX;

        // If ball hits the top, bounce it. 
        if (ballY + ballDY < 15) ballDY = -ballDY;
        // If the ball hits the bottom, check see if it hits a paddle.
        else if (ballY + ballDY > boardY - 15) {
          // If the ball hits the paddle, bounce it.
          if (ballX > paddleX && ballX < paddleX + paddleW) ballDY = -ballDY;
          // Otherwise the game is over.
          else {
            clearInterval(gameLoop);
            alert("Game over!");
          }
        }
      }



      // Get key press.


      function whatKey(evt) {

        switch (evt.keyCode) {
          // Left arrow.
        case 37:
          paddleX = paddleX - 20;
          if (paddleX < 0) paddleX = 0;
          paddle.setAttribute("x", paddleX);
          break;

          // Right arrow.
        case 39:
          paddleX = paddleX + 20;
          if (paddleX > boardX - paddleW) paddleX = boardX - paddleW;
          paddle.setAttribute("x", paddleX);
          break;
        }
      }
    </script>
  </head>
  
  <body onload="drawGameSVG()">
    <h1>
      SVG Racketball
    </h1>
    <!-- Create the SVG pane. -->
    <svg xmlns="http://www.w3.org/2000/svg" height="300" width="300">
      <!-- Create the board. -->
      <rect x="1" y="1" width="298" height="298" fill="thistle" id="bpard" />
      <!-- Create the ball. -->
      <circle cx="150" cy="150" r="15" fill="tomato" id="ball" />
      <!-- Create the paddle. -->
      <rect x="150" y="290" width="150" height="10" fill="navy" id="paddle"
      />
    </svg>
  </body>

</html>

SVG Code Sample Discussion

The JavaScript logic used in the Canvas game sample is adapted for this SVG game sample with only a few changes. The following sections detail which parts are the same and which are different.

This SVG code sample uses a standard HTML5 header: <!doctype html>, so that browsers can distinguish it as part of the HTML5 specification.

This code is divided into two major parts:

  • Body Code
  • Script Code

Body Code

This contains the definitions for the SVG shapes that the game uses: board, circle, and paddle.

Script Code

The script code contains the global variables, the drawGameSVG function, the drawBall function, and the whatKey function.

Global Variables

This is the same as the Canvas sample.

drawGameSVG Function

This is called on page load and replaces the drawGameCanvas function. Because the board, ball, and paddle are created in the body, all this does is call the drawBall game loop and set up the whatKey event listener.

drawBall Function

Most of this is the same as the Canvas drawBall function except that the ball’s x and y attributes are changed with setAttribute and, unlike Canvas, the SVG ball is not redrawn every time, but is only moved. This difference takes advantage of the SVG retained mode because the ball and its attributes are stored in memory. Change an attribute and the object changes instantly. The rest of the game loop contains the logic that determines where the ball will be the next time the game loop is processed.

whatKey Function

Most of this is the same as the Canvas drawBall function except that the ball’s x and y attributes are changed with setAttribute and, unlike Canvas, the SVG ball is not redrawn every time, but is only moved. This difference takes advantage of the SVG retained mode because the ball and its attributes are stored in memory. Change an attribute and the object changes instantly. The rest of the game loop contains the logic that determines where the ball will be the next time the game loop is processed.

Comparison Summary

Both Canvas and SVG can produce fast-moving, action-packed games. The main differences between the code samples discussed here are how they handle putting images on the screen. The retained mode SVG images are created in the body and they are modified by setting the attributes of the ball and paddle using the setAttribute method. Canvas‘s immediate mode requires that the ball is redrawn each time the game loop is called, but SVG only needs a few simple calls to change the position of the paddle and ball.

When creating a game on a small board size with only a few objects, there is virtually no performance difference between Canvas and SVG. The Canvas program is 134 lines and the SVG is 115. However, as you create more objects, the Canvas code will grow much larger. And since the Canvas objects have to be redrawn every time you go through the game loop, the Canvas game will slow down.

How to Choose Between Canvas and SVG to Create Web Graphics