Create the Classic Space Invaders Game with JavaScript and HTML | by Nicolai B. Andersen | Jun, 2022

A tutorial to recreate Space Invaders with HTML and JavaScript

A GIF showing the game created in the article.

Do you want to add a simple 2D game to your website? Or do you want to learn the basics of creating 2D games? This article explains how to recreate the classic Space Invaders game for most modern browsers with only HTML, and JavaScript.

It can be a great idea to specify the basic features of a game before starting the development to get an overview of the work that needs to be done.

The game’s specifications can be defined as follows:

  • The game requires an object that can be moved, drawn to the canvas’ surface, and provides collision checks.
  • The enemy and player objects should be able to fire bullets toward each other.
  • A bullet object should only be able to move in one direction.
  • The player object should only be able to move left and right on the x-axis within the canvas’ borders.
  • An asteroid object should consist of several destroyable parts.
  • The game should restart if all the enemies are destroyed, or if one of the enemies reaches the position of the player on the y-axis.

The game’s objects

The class diagram in figure 1 shows an overview of the game’s different classes and their relations. It can for example be seen that all visual objects are created by the base class GameObject, or that the Player class is inheriting properties and behavior from both the SpaceShip and the GameObject class.

Figure 1: Shows a UML class diagram of the game.

The first step is to set up the HTML document with a canvas element (See figure 2). An HTML canvas provides the ability to draw various 2D graphics (Mozilla, 2022a).

It is for example possible to draw simple shapes or images inside the canvas (Mozilla, 2022b) (Mozilla, 2022c). The canvas element in figure 2, line 9, is defined with the id, widthand height properties.

The id property is used to reference the canvas element in JavaScript, and the width and height properties simply specify the dimensions of the canvas.

Figure 2: Shows a general HTML document with a canvas element.

The next step is to set up a JavaScript object that can be used to hold different properties and behavior (Mozilla, 2022d). Figure 3, line 12, shows an implementation of the Javascript object, which is called game.

The idea behind using a JavaScript object to hold properties and behavior in this tutorial is to avoid the game’s logic being available as a lot of global variables and functions.

General properties

Three properties are added to the game object on lines 15–19, in figure 3. The canvas property refers to the HTML canvas element, ctx refers to the interface, that can be used to draw on the canvas surface (Mozilla, 2022e), and backgroundColor refers to the canvas’ background color.

General behavior

Five methods are added to the game object on lines 22–43, figure 3. update() defines the game’s continuous behavior, such as drawing elements, moving elements, etc. The first two lines within the update() method (lines 24–25, figure 3) set the background of the canvas to the value of the backgroundColor property.

Redrawing the background for each time update() is called ensures objects which are moving will be drawn at the new position instead of both the new and old position.keydown() defines what should happen if a key on the keyboard was pressed. init() defines the initialization of the game which starts the game loop. stop() defines how to stop the game loop, and restart() defines how to restart the game.

Figure 3: Shows the implementation of a JavaScript object used to handle the game’s initialization, and runtime behavior.

A class called GameObject is added to define the general behavior of objects used within the game. The general game object contains five properties. x and y defines the position of the game object. width and height defines the size of the game object’s rectangle, and color defines the color of the rectangle.

The GameObject class also contains three methods. draw() is used to draw the game object within the canvas. update(dx, dy) move the game object in any given direction, and collidesWith(obj) returns true if a given game object overlaps with the game object.

Figure 4: Shows an implementation of a general game object class that can draw a rectangle, update its position, and detect collisions with other game objects.

After adding the GameObject class, a simple Bullet class can be added which inherits the properties and behavior from the GameObject class.

The Bullet class contains an additional property called dy which is used to define the direction the bullet should move on the y axis. The update(dx, dy) method inherited from the GameObject class is overridden in the bullet class (see lines 22–25, figure 5) to define it always should move towards the given start direction.

Figure 5: Shows the implementation of the Bullet class.

A class called SpaceShip is added to define the general properties and behavior of the player and enemy objects. The SpaceShip class is also a subclass of the GameObject class.

The SpaceShip class has five additional properties. canvasHeight is used to specify the boundaries for the bullets fired by a SpaceShip. bulletWidth and bulletHeight defines the dimension of the bullets. bulletColor defines the color of the bullets, and bullets is an array used to specify bullets spawned by a SpaceShip.

The SpaceShip class is designed to override the draw(ctx) method inherited from the GameObject class to implement a check removing bullets outside of the screen and to implement a loop drawing and moving bullets.

The SpaceShip class also includes an additional method called shoot(dy) which is used to spawn bullets moving towards the given direction on the y axis.

Figure 6: Shows the implementation of the SpaceShip class.

A Player class is added as a subclass of the SpaceShip class to implement properties and behavior specific to the player object.

The Player class has an additional property called canvasWidth defining the width of the game’s canvas.

Figure 7: The green line represents the path where the player can move.

The property is used to check if the player is outside of the screen on the x-axis. The check is implemented by overriding the update(dy, dx) method inherited from the parent class and adding some additional lines of code moving the player position on the x-axis either to zero or the canvas width minus the player width.

Figure 8: Shows the implementation of the Player Class.

The last class needed is the Asteroid class. This class is not a subclass of the game object class, however, the class is closely connected to the game object class, because it contains a property called parts which is an array of game objects.

This design allows an asteroid in the game to be destroyed part by part (see figure 9).

Figure 9: Shows an example of the player shooting an asteroid.

The Asteroid class also contains three methods. draw(ctx) is used to draw the asteroid’s game objects. collidesWith(obj) returns true if one of the asteroid’s game objects collides with the given game object, and removeOnCollide(obj) removes one of the asteroid’s game objects if one of the asteroid’s game objects collids with the given game object.

Figure 10: Shows the implementation of the Asteroid class.

Figure 11, lines 24–45 shows the implementation of the additional required game properties for the asteroids and enemies.

Additional Asteroid properties

asteroidsParts defines the number of game objects an asteroid is made of, noOfAsteroids is used to define the number of asteroids the game should contain, and asteroidsSpace defines the space between each asteroid.

Additional enemy properties

enemiesEachLine define the number of enemies that should be spawned on each line. enemyLines defines the number of lines of enemies. enemySpace defines the space between each enemy. enemyFireRate defines how often enemies should fire a bullet, and enemyFireTimer is a variable used to keep track of the time since the last shot. enemyDirection defines if the enemies should move left or right (left = -1, right = 1), and enemyStep defines how much the enemies should be moved on the y-axis when reaching either the left or right side of the canvas.

Figure 11: Shows the additional game properties for the asteroids and enemies.

It is now possible to complete the game object’s init() method (see lines 27–62, figure 12). The method should include some additional lines of code defining how to create the player, the enemies, and the asteroids, at their start position.

This design allows using the game object’s restart() method, to reset the game to the initial state.

Figure 12: Shows the implementation of the game’s initialization behavior.

The game object’s keydown() method is called every time a user presses a key on the keyboard (see lines 23–36, figure 13). The specific key pressed can be read as a number from the argument e by reading its property keyCode.

For example, the number 37 defines the left arrow key, and the number 39 defines the right arrow key. The keydown() method is designed to check if the user pressed one of the arrow keys, or the A-key and D-key, which moves the player either left or right. It also includes an additional check to detect whenever the user presses the space key which spawns a bullet at the player’s position.

Figure 13: Shows the implementation of the game’s keyboard detection behavior.

The final method of the game that needs modification is the game object’s update() method (see lines 23–154, figure 14).

Draw the player, asteroids, and enemies

Lines 29–38, figure 14, simply call the draw(ctx) method on the different objects to ensure they are drawn within the canvas. Line 39, figure 14, in the same loop drawing the enemies, moves the enemies towards the direction specified in the enemyDirection property.

Enemy count check

Lines 43–46, figure 14, check if the game object’s enemies array is empty, and restarts the game if the check evaluates to true. This check could include some additional code decreasing a player’s life property until it reaches zero, which then would restart the game.

Enemy movement

Lines 49–91, figure 14, check if the enemyDirection property is either 1 or -1. If the property is 1, the method finds the enemy closest to the right side of the screen, and if that enemy reaches the right side, the method sets the enemyDirection to -1 and moves all the enemies down by the value of enemyStep. The opposite happens if the value of enemyDirection is -1.

Enemy random shot

Lines 94–99, figure 14, increase the enemyTimer property until a random enemy is selected to fire a shot that moves towards the player.

Bullet collision checks

Lines 102–154, figure 14, show four loops with almost the same behaviour. For example, the first loop, lines 101–110, figure 14, loops through all player bullets and checks if they collide with one of the astroids’ game objects. The third loop, lines 125–134, figure 14, loops through all player bullets and checks if one of the bullets collids with one of the enemies.

Figure 14: Shows the implementation of the game’s continuous behavior.

The complete code can be found at this link.

Mozilla. 2022a. Canvas API.
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API.
Accessed: 28th June 2022

Mozilla. 2022b. Using images.
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images.
Accessed: 28th June 2022

Mozilla. 2022c. Drawing shapes with canvas.
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes.
Accessed: 28th June 2022

Mozilla. 2022d. Working with objects.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects.
Accessed: 28th June 2022

Mozilla. 2022e. CanvasRenderingContext2D.
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.
Accessed: 28th June 2022

Leave a Comment