Learn CreateJS by Building an HTML5 Pong Game

The web moves fast - so fast that our original EaselJS tutorial is already out of date! In this tutorial, you'll learn how to use the newest CreateJS suite by creating a simple Pong clone.

Final Result Preview

Let's take a look at the final result we will be working towards:

Click to play

This tutorial is based on Carlos Yanez's Create a Pong Game in HTML5 With EaselJS, which in turn built on his Getting Started With EaselJS guide. The graphics and sound effects are all taken from the former tutorial.

Step 1: Create index.html

This will be our main index.html file:

 1 2   3   4   5  Pong  6   7   8 9   10   11   12   13   14   15   16   17   18   19   20   21  

As you can see, it’s pretty short and consists mainly of loading the CreateJS libraries.

Since the release of CreateJS (which basically bundles all the separate EaselJS libraries) we no longer have to download the JS files and host them on our website; the files are now placed in a CDN (Content Delivery Network) which allows us to load these files remotely as quickly as possible.

Let’s review the code:

 1 2  

This line removes the mobile highlight which may appear when you trying to play the game on mobile. (The mobile highlight causes the canvas object to get highlighted and thus ignore your finger movements.)

 1 2   3   4   5   6  

This code loads the JS files from the CreateJS CDN and it basically allows us to use any of the CreateJS functions in our code

Next, we will load the SoundJS Flash plugin, which provides sound support for browsers that don’t support HTML5 Audio. This is done by using a SWF (a Flash object) to load the sounds.

 1 2  

In this case we will not use the CDN; instead, we’ll download the SoundJS library from http://createjs.com/#!/SoundJS/download and place the soundjs.flashplugin-0.2.0.min.js and FlashAudioPlugin.swf files in a local folder named assets.

Last among the JS files, we’ll load the Main.js file which will contain all the code to our game:

 1 2  

Finally, let’s place a Canvas object on our stage.

 1 2   3   4  

Now we can start working on the game code.

Step 2: The Variables

Our game code will be inside a file named Main.js, so create and save this now.

First of all, let’s define variables for all the graphic objects in the game:

 1 2 var canvas; //Will be linked to the canvas in our index.html page  3 var stage; //Is the equivalent of stage in AS3; we'll add "children" to it  4 5 // Graphics  6 //[Background]  7 8 var bg; //The background graphic  9 10 //[Title View]  11   12 13 var main; //The Main Background  14 var startB; //The Start button in the main menu  15 var creditsB; //The credits button in the main menu  16 17 //[Credits]  18 19 20 var credits; //The Credits screen  21 22 //[Game View]  23 24 25 var player; //The player paddle graphic  26 var ball; //The ball graphic  27 var cpu; //The CPU paddle  28 var win; //The winning popup  29 var lose; //The losing popup 

I’ve added a comment for each variable so that you’ll know what we’ll be loading in that variable

Next up, the scores:

 1 2 //[Score]  3 4 var playerScore; //The main player score  5 var cpuScore; //The CPU score  6 var cpuSpeed=6; //The speed of the CPU paddle; the faster it is the harder the game is 

We’ll, need variables for the speed of the ball:

 1 2 // Variables  3 4 var xSpeed = 5;  5 var ySpeed = 5; 

You can change these values to whatever you want, if you’d like to make the game easier or harder.

If you're a Flash developer, you know that Flash’s onEnterFrame is very useful when creating games, as there are things that need to happen in every given frame. (If you're not familiar with this idea, check out this article on the Game Loop.)

We have an equivalent for onEnterFrame in CreateJS, and that is the ticker object, which can run code every fraction of a second. Let’s create the variable that will link to it:

 1 2 var tkr = new Object; 

Next we have the preloader, which will use the new PreloadJS methods.

 1 2 //preloader  3 var preloader;  4 var manifest;  5 var totalLoaded = 0; 
• preloader - will contain the PreloadJS object.
• manifest - will hold the list of files we need to load.
• totalLoaded - this variable will hold the number of files already loaded.

Last but not least in our list of variables, we have TitleView, which will hold several graphics within in order to display them together (like a Flash DisplayObjectContainer).

 1 2 var TitleView = new Container(); 

Let’s move on to the Main function...

Step 3: The Main() Function

This function is the first function that runs after all the JS files from the index.html are loaded. But what's calling this function?

Well, remember this line from the index.html file?

 1 2  

This code snippet states that once the HTML (and JS libraries) are loaded, the Main function should run.

Let's review it:

 1 2 function Main()  3 {  4  /* Link Canvas */  5   6  canvas = document.getElementById('PongStage');  7  stage = new Stage(canvas);  8   9  stage.mouseEventsEnabled = true;  10   11   12  /* Set The Flash Plugin for browsers that don't support SoundJS */  13  SoundJS.FlashPlugin.BASE_PATH = "assets/";  14  if (!SoundJS.checkPlugin(true)) {  15  alert("Error!");  16  return;  17  }  18 19  manifest = [  20  {src:"bg.png", id:"bg"},  21  {src:"main.png", id:"main"},  22  {src:"startB.png", id:"startB"},  23  {src:"creditsB.png", id:"creditsB"},  24  {src:"credits.png", id:"credits"},  25  {src:"paddle.png", id:"cpu"},  26  {src:"paddle.png", id:"player"},  27  {src:"ball.png", id:"ball"},  28  {src:"win.png", id:"win"},  29  {src:"lose.png", id:"lose"},  30  {src:"playerScore.mp3|playerScore.ogg", id:"playerScore"},  31  {src:"enemyScore.mp3|enemyScore.ogg", id:"enemyScore"},  32  {src:"hit.mp3|hit.ogg", id:"hit"},  33  {src:"wall.mp3|wall.ogg", id:"wall"}  34  ];  35 36 37 38  preloader = new PreloadJS();  39  preloader.installPlugin(SoundJS);  40  preloader.onProgress = handleProgress;  41  preloader.onComplete = handleComplete;  42  preloader.onFileLoad = handleFileLoad;  43  preloader.loadManifest(manifest);  44 45  /* Ticker */  46   47  Ticker.setFPS(30);  48  Ticker.addListener(stage);  49 } 

Let’s break down each part:

 1 2  canvas = document.getElementById('PongStage');  3  stage = new Stage(canvas);  4   5  stage.mouseEventsEnabled = true; 

Here we link the PongStage Canvas object from the index.html file to the canvas variable, and then create a Stage object from that canvas. (The stage will allow us to place objects on it.)

mouseEventsEnabled enables us to use mouse events, so we can detect mouse movements and clicks.

 1 2 /* Set The Flash Plugin for browsers that don't support SoundJS */  3  SoundJS.FlashPlugin.BASE_PATH = "assets/";  4  if (!SoundJS.checkPlugin(true)) {  5  alert("Error!");  6  return;  7  } 

Here we configure where the Flash sound plugin resides for those browsers in which HTML5 Audio is not supported

 1 2  manifest = [  3  {src:"bg.png", id:"bg"},  4  {src:"main.png", id:"main"},  5  {src:"startB.png", id:"startB"},  6  {src:"creditsB.png", id:"creditsB"},  7  {src:"credits.png", id:"credits"},  8  {src:"paddle.png", id:"cpu"},  9  {src:"paddle.png", id:"player"},  10  {src:"ball.png", id:"ball"},  11  {src:"win.png", id:"win"},  12  {src:"lose.png", id:"lose"},  13  {src:"playerScore.mp3|playerScore.ogg", id:"playerScore"},  14  {src:"enemyScore.mp3|enemyScore.ogg", id:"enemyScore"},  15  {src:"hit.mp3|hit.ogg", id:"hit"},  16  {src:"wall.mp3|wall.ogg", id:"wall"}  17  ]; 

In the manifest variable we place an array of files we want to load (and provide a unique ID for each one). Each sound has two formats - MP3 and OGG - because different browsers are (in)compatible with different formats.

 1 2  preloader = new PreloadJS();  3  preloader.installPlugin(SoundJS);  4  preloader.onProgress = handleProgress;  5  preloader.onComplete = handleComplete;  6  preloader.onFileLoad = handleFileLoad;  7  preloader.loadManifest(manifest); 

We create a new PreloadJS object and place it in the preloader variable, then assign a method to each event (onProgress, onComplete, onFileLoad). Finally we use the preloader to load the manifest we created earlier.

 1 2 Ticker.setFPS(30);  3  Ticker.addListener(stage); 

Here we add the Ticker object to the stage and set the frame rate to 30 FPS; we’ll use it later in the game for the enterFrame functionality.

Step 4: Creating the Preloader Functions

 1 2 function handleProgress(event)  3 {  4  //use event.loaded to get the percentage of the loading  5 }  6 7 function handleComplete(event) {  8  //triggered when all loading is complete  9 }  10 11 function handleFileLoad(event) {  12  //triggered when an individual file completes loading  13   14  switch(event.type)  15  {  16  case PreloadJS.IMAGE:  17  //image loaded  18  var img = new Image();  19  img.src = event.src;  20  img.onload = handleLoadComplete;  21  window[event.id] = new Bitmap(img);  22  break;  23 24  case PreloadJS.SOUND:  25  //sound loaded  26  handleLoadComplete();  27  break;  28  }  29 } 

Let’s review the functions:

• handleProgress - In this function you’ll be able to follow the percentage of the loading progress using this parameter: event.loaded. You could use this to create for example a progress bar.
• handleComplete - This function is called once all the files have been loaded (in case you want to place something there).
• handleFileLoad - Since we load two types of files - images and sounds - we have this function that will handle each one separately. If it’s an image, we create a bitmap image and place it in a variable (whose name is the same as the ID of the loaded image) and then call the handleLoadComplete function (which we'll write next); if it’s a sound then we just call the handleLoadComplete immediately.

Now let’s discuss the handleLoadComplete function I just mentioned:

 1 2  function handleLoadComplete(event)  3  {  4 5  totalLoaded++;  6   7  if(manifest.length==totalLoaded)  8  {  9  addTitleView();  10  }  11  } 

It’s a pretty straightforward function; we increase the totalLoaded variable (that holds the number of assets loaded so far) and then we check if the number of items in our manifest is the same as the number of loaded assets, and if so, go to the Main Menu screen.

Step 5: Creating the Main Menu

 1 2 function addTitleView()  3 {  4  //console.log("Add Title View");  5  startB.x = 240 - 31.5;  6  startB.y = 160;  7  startB.name = 'startB';  8   9  creditsB.x = 241 - 42;  10  creditsB.y = 200;  11   12  TitleView.addChild(main, startB, creditsB);  13  stage.addChild(bg, TitleView);  14  stage.update();  15   16  // Button Listeners  17   18  startB.onPress = tweenTitleView;  19  creditsB.onPress = showCredits; 

Nothing special here. We place the images of the Background, Start Button and Credits Button on the stage and link onPress event handlers to the Start and Credits buttons.

Here are the functions that display and remove the credits screen and the tweenTitleView which starts the game:

 1 2 function showCredits()  3 {  4  // Show Credits  5   6  credits.x = 480;  7   8  stage.addChild(credits);  9  stage.update();  10  Tween.get(credits).to({x:0}, 300);  11  credits.onPress = hideCredits;  12 }  13 14 // Hide Credits  15 16 function hideCredits(e)  17 {  18  Tween.get(credits).to({x:480}, 300).call(rmvCredits);  19 }  20 21 // Remove Credits  22 23 function rmvCredits()  24 {  25  stage.removeChild(credits);  26 }  27 28 // Tween Title View  29 30 function tweenTitleView()  31 {  32  // Start Game  33   34  Tween.get(TitleView).to({y:-320}, 300).call(addGameView);  35 } 

Step 6: The Game Code

We’ve reached the main part of this tutorial which is the code of the game itself.

First of all, we need to add all the required assets to the stage, so we do that in the addGameView function:

 1 2 function addGameView()  3 {  4  // Destroy Menu & Credits screen  5   6  stage.removeChild(TitleView);  7  TitleView = null;  8  credits = null;  9   10  // Add Game View  11   12  player.x = 2;  13  player.y = 160 - 37.5;  14  cpu.x = 480 - 25;  15  cpu.y = 160 - 37.5;  16  ball.x = 240 - 15;  17  ball.y = 160 - 15;  18   19  // Score  20   21  playerScore = new Text('0', 'bold 20px Arial', '#A3FF24');  22  playerScore.x = 211;  23  playerScore.y = 20;  24   25  cpuScore = new Text('0', 'bold 20px Arial', '#A3FF24');  26  cpuScore.x = 262;  27  cpuScore.y = 20;  28   29  stage.addChild(playerScore, cpuScore, player, cpu, ball);  30  stage.update();  31   32  // Start Listener  33   34  bg.onPress = startGame;  35 } 

Again, a pretty straightforward function that places the objects on the screen and adds a mouseEvent to the background image, so that when the user clicks it the game will start (we will call the startGame function).

Let’s review the startGame function:

 1 2 function startGame(e)  3 {  4  bg.onPress = null;  5  stage.onMouseMove = movePaddle;  6   7  Ticker.addListener(tkr, false);  8  tkr.tick = update;  9 } 

Here, as you can see, in addition to adding an onMouseMove event that will move our paddle. We add the tick event, which will call the update function in each frame.

Let’s review the movePaddle and reset functions:

 1 2 function movePaddle(e)  3 {  4  // Mouse Movement  5  player.y = e.stageY;  6 }  7 8 /* Reset */  9 10 function reset()  11 {  12  ball.x = 240 - 15;  13  ball.y = 160 - 15;  14  player.y = 160 - 37.5;  15  cpu.y = 160 - 37.5;  16   17  stage.onMouseMove = null;  18  Ticker.removeListener(tkr);  19  bg.onPress = startGame;  20 } 

In movePaddle, we basically place the user’s paddle at the mouse's y-coordinate.

In reset, we do something similar to addGameView, except here we don’t add any graphic elements since they are already on the screen.

Using the alert function we’ll display the winning and losing popup:

 1 2 function alert(e)  3 {  4  Ticker.removeListener(tkr);  5  stage.onMouseMove = null;  6  bg.onPress = null  7   8  if(e == 'win')  9  {  10  win.x = 140;  11  win.y = -90;  12   13  stage.addChild(win);  14  Tween.get(win).to({y: 115}, 300);  15  }  16  else  17  {  18  lose.x = 140;  19  lose.y = -90;  20   21  stage.addChild(lose);  22  Tween.get(lose).to({y: 115}, 300);  23  }  24 } 

Step 7: The Game Loop

Now, for the last part of our tutorial we’ll work on the update function (which occurs in every frame of the game - similar to Flash’s onEnterFrame):

 1 2 function update()  3 {  4  // Ball Movement  5 6  ball.x = ball.x + xSpeed;  7  ball.y = ball.y + ySpeed;  8   9  // Cpu Movement  10   11  if(cpu.y < ball.y) {  12  cpu.y = cpu.y + 4;  13  }  14  else if(cpu.y > ball.y) {  15  cpu.y = cpu.y - 4;  16  }  17   18  // Wall Collision  19 20  if((ball.y) < 0) { ySpeed = -ySpeed; SoundJS.play('wall'); };//Up  21  if((ball.y + (30)) > 320) { ySpeed = -ySpeed; SoundJS.play('wall');};//down  22   23  /* CPU Score */  24   25  if((ball.x) < 0)  26  {  27  xSpeed = -xSpeed;  28  cpuScore.text = parseInt(cpuScore.text + 1);  29  reset();  30  SoundJS.play('enemyScore');  31  }  32   33  /* Player Score */  34   35  if((ball.x + (30)) > 480)  36  {  37  xSpeed = -xSpeed;  38  playerScore.text = parseInt(playerScore.text + 1);  39  reset();  40  SoundJS.play('playerScore');  41  }  42   43  /* Cpu collision */  44   45  if(ball.x + 30 > cpu.x && ball.x + 30 < cpu.x + 22 && ball.y >= cpu.y && ball.y < cpu.y + 75)  46  {  47  xSpeed *= -1;  48  SoundJS.play('hit');  49  }  50   51  /* Player collision */  52   53  if(ball.x <= player.x + 22 && ball.x > player.x && ball.y >= player.y && ball.y < player.y + 75)  54  {  55  xSpeed *= -1;  56  SoundJS.play('hit');  57  }  58   59  /* Stop Paddle from going out of canvas */  60   61  if(player.y >= 249)  62  {  63  player.y = 249;  64  }  65   66  /* Check for Win */  67   68  if(playerScore.text == '10')  69  {  70  alert('win');  71  }  72   73  /* Check for Game Over */  74   75  if(cpuScore.text == '10')  76  {  77  alert('lose');  78  }  79 } 

Looks scary, doesn’t it? Don’t worry, we’ll review each part and discuss it.

 1 2  // Ball Movement  3 4  ball.x = ball.x + xSpeed;  5  ball.y = ball.y + ySpeed; 

In each frame, the ball will move according to its x and y speed values

 1 2 // Cpu Movement  3   4  if((cpu.y+32) < (ball.y-14)) {  5  cpu.y = cpu.y + cpuSpeed;  6  }  7  else if((cpu.y+32) > (ball.y+14)) {  8  cpu.y = cpu.y - cpuSpeed;  9  } 

Here we have the basic AI of the computer, in which the computer’s paddle simply follows the ball without any special logic. We just compare the location of the center of the paddle (which is why we add 32 pixels to the cpu Y value) to the location of the ball, with a small offset, and move the paddle up or down as necessary.

 1 2 if((ball.y) < 0) { //top  3  ySpeed = -ySpeed;  4  SoundJS.play('wall');  5 };  6 if((ball.y + (30)) > 320) { //bottom  7  ySpeed = -ySpeed;  8  SoundJS.play('wall');  9 }; 

If the ball hits the top border or the bottom border of the screen, the ball changes direction and we play the Wall Hit sound.

 1 2  /* CPU Score */  3  if((ball.x) < 0)  4  {  5  xSpeed = -xSpeed;  6  cpuScore.text = parseInt(cpuScore.text + 1);  7  reset();  8  SoundJS.play('enemyScore');  9  }  10  /* Player Score */  11  if((ball.x + (30)) > 480)  12  {  13  xSpeed = -xSpeed;  14  playerScore.text = parseInt(playerScore.text + 1);  15  reset();  16  SoundJS.play('playerScore');  17  } 

The score login is simple: if the ball passes the left or right borders it increases the score of the player or CPU respectively, plays a sound, and resets the location of the objects using the reset function we’ve discussed earlier.

 1   2 /* CPU collision */  3 if(ball.x + 30 > cpu.x && ball.x + 30 < cpu.x + 22 && ball.y >= cpu.y && ball.y < cpu.y + 75)  4 {  5  xSpeed *= -1;  6  SoundJS.play('hit');  7 }  8 /* Player collision */  9 if(ball.x <= player.x + 22 && ball.x > player.x && ball.y >= player.y && ball.y < player.y + 75)  10 {  11  xSpeed *= -1;  12  SoundJS.play('hit');  13  } 

Here we deal with collisions of the ball with the paddles; every time the ball hits one of the paddles, the ball changes direction and a sound is played

 1 2  if(player.y >= 249)  3  {  4  player.y = 249;  5  } 

If the player's paddle goes out of bounds, we place it back within the bounds.

 1 2  /* Check for Win */  3  if(playerScore.text == '10')  4  {  5  alert('win');  6 }  7 /* Check for Game Over */  8 if(cpuScore.text == '10')  9 {  10  alert('lose');  11 } 

In this snippet, we check whether either of the players’ score has reached 10 points, and if so we display the winning or losing popup to the player (according to his winning status).

Conclusion

That’s it, you’ve created an entire pong game using CreateJS. Thank you for taking the time to read this tutorial.