In this project, we are going to build a card quest game using HTML elements and CSS styling. We will also learn how to separate JavaScript logic into modules for clarity. We will build the game from scratch with a basic HTML layout and then represent the composition of the pattern in logic and CSS.
We are going to create a matching game. This game presents a combination of patterns to the player, and some of these patterns overlap each other. Players analyze the patterns and then select them in the correct sequence so that the composition matches the provided one. You may visit the URL http://makzan.net/html5-games/color-quest/ to play the example game in order to have a better understanding of what we will build throughout this project.
The following screenshot shows the final result of this project. The upper part is a timer, the part to the left shows the quest, the right part is the composition of the player, and the lower part is the deck of patterns.

A simple matching game like this is a perfect way to get you warmed up with the HTML5 games' development rhythm. What we will learn in this project is the foundation to build more complicated games later in this book.
This game uses what we are familiar with: HTML element interaction and styling in CSS. What's new, is JavaScript logic. This project will show us how to convert the game rule into code. We will also learn how to separate logic into different parts with different responsibilities.
We'll perform the following tasks in this project:
In this game, we are going to stick to plain JavaScript without using a library. This will help us get started with a basic foundation in JavaScript.
Note that writing JavaScript for games is a little bit different from writing it for a web page. Game logic requires well-structured code to ensure the logic is easy to read and maintained. The following essays provide an in-depth discussion on writing JavaScript in the right way:
JavaScript best practices: http://www.thinkful.com/learn/javascript-best-practices-1/
JavaScript the right way: http://www.jstherightway.org
In this task, we are going to kick-start the project by creating the file structure, ensuring that the essential files are ready.
We need several things to get started. First, we will create an empty project directory. Then, we will create an index.html
file, a folder where we will put the CSS styling files, and a folder to put the JavaScript logic files in.
During the missions, we need several graphics files, including the background and buttons; you can find the graphics from the sample code. Put all the images into a directory named images
. The created file structure is shown in the following screenshot:

Use the following steps to create the basic game structure:
We will enter the following HTML code in the
index.html
file. It is a basic HTML structure that includes the CSS file in the head and the script tag at the bottom:<!DOCTYPE html> <html lang='en'> <head> <meta charset='utf-8'> <title>Color Quest</title> <link rel="stylesheet" href="game.css"> </head> <body> <!-- game content here --> <script src='js/game.js'></script> </body> </html>
Right after the opening of the
<body>
tag and before our<script>
tag, we add the following HTML code for the game page. The content is divided into four parts: header, game section, the how-to-play section, and footer. The game section is where all the game logic happens. The#element-template
is the template of game elements that will be used for cloning later:<header> <div class="row"> <h1>Color Quest</h1> </div> </header> <section id="game"> </section> <section class='how-to-play'> <div class="row"> <h2>How to Play?</h2> <p>Composite your card to match the given pattern.</p> </div> </section> <footer> <div class="row"> <p>This game is an example for the HTML5 Games Hotshot book. Free for personal and commerical use.</p> </div> </footer> <div id='element-template'> </div>
Add the following JavaScript to the
game.js
file. It acts as the entry point of the game logic:(function(){ // Entry Point var init = function() { }; init(); // start the game })(); // self-executing function.
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
We have created a very basic structure of the game. The following sections explain what we have done.
The header, section, and footer arrangement follows a simple HTML5 content structure. We will put the game elements inside this structure, attached to the game. The following screenshot shows the HTML structure:

We separate the JavaScript logic into modules, and each separated file encapsulates one module. This allows us to tackle each part individually. The first JavaScript file we have is the game.js
file. It is in charge of controlling the game flow. There are other different parts in this game, and for these parts, we are going to create different files with the purpose of using them for future tasks.
In JavaScript, we want to have as few global variables as possible. Variables are global when they are attached to the highest scope of the runtime environment. In a web browser, this refers to a variable that is attached to a window
scope. A variable that is created with the var
keyword lives inside the scope of the function that encloses it.
This is why we put all the logic inside a self-executing anonymous function. This ensures that by default, we will not pollute the global scope with our game logic variables. The following code block shows how to create each module with one global game variable and one local game variable inside a self-executing-anonymous function:
(function(){ var game = this.colorQuestGame = this.colorQuestGame || {}; })();
We will intentionally create one global variable named colorQuestGame
to act as the namespace. In the later sections, we will put different logic modules into different files under the same global object.
The this.colorQuestGame || {};
declaration means that, by default, it will use the existing colorQuestGame
variable if it was already declared previously. Otherwise, it will be a new empty object. We will put this line of declaration into every file.
This scoping feature is also useful when we need to encapsulate logic into private functions. For example, the following code shows how we can create private functions that are only accessible inside a specific scope to help extract the code:
Composition.createFromSequence = function(sequence) { // helper functions var privateHelperA = function() {} var privateHelperB = function() {} // end helper functions // use your private helper functions here. }
Inside any method, we can declare local scoped functions to help extract the logic and to make the code more readable.
For performance, we usually place scripts at the end of Document Object Model (DOM) and just before the closing of the </body>
tag. This is because script loading may block the loading of DOM and cause the webpage to load slower. For more details, please have a look at the Yahoo performance rule, Put Scripts at the Bottom, at http://developer.yahoo.com/performance/rules.html#js_bottom.
In this task, we create four scenes and display different scenes based on the game flow.
The following figure is our planning of the four scenes and the flow, showing on how they should link together:

The following figure shows three scenes of what we will create in this task:

We code the management part of the scene via the following steps:
The scenes are DOM elements, so we will have the following HTML elements defined inside the tag with the
game
ID:<div id="game-scene" class="scene out"> <a href="#" id="gameover-btn">Game Over</a> <a href="#" id="finish-btn">Finish</a> </div> <div id="start-scene" class="scene"> <a href="#" id="start-btn" class="button">Start Game</a> </div> <div id="summary-scene" class="scene out"> <a href="#" id="next-level-button" class="button">Next</a> </div> <div id="gameover-scene" class="scene out"> <a href="#" id="back-to-menu-button" class="button">Back to menu</a> </div>
Now, we need to import our newly created
scenes.js
file into the HTML file, before thegame.js
file:<script src='js/scenes.js'></script> <script src='js/game.js'></script> </body>
In the
scene.js
file, we add the following code to define the scene's object and its instances:(function(){ var game = this.colorQuestGame = this.colorQuestGame ||{}; // put common scene logic into 'scene' object. var scene = { node: document.querySelector('.scene'), show: function() { this.node.classList.remove('out'); this.node.classList.add('in'); }, hide: function() { this.node.classList.remove('in'); this.node.classList.add('out'); } }; // scene instances code to go here. )();
Then, we create an instance of the game scene. Put the following code right after the
scene
object code. The following code creates two temporary links to finish the level and complete the game:var gameScene = game.gameScene = Object.create(scene); gameScene.node = document.getElementById('game-scene'); gameScene.handleInput = function() { document.getElementById('finish-btn').onclick = function(){ game.flow.finishLevel(); }; document.getElementById('gameover-btn').onclick = function(){ game.flow.gameOver(); }; };
The start scene instance comes after the game scene code. The following code handles the clicking of the start button that links to the game scene:
var startScene = game.startScene = Object.create(scene); startScene.node = document.getElementById('start-scene'); startScene.handleInput = function() { document.getElementById('start-btn').onclick = function(){ game.flow.nextLevel(); }; };
Then, we have the summary scene. The summary scene has a button that links to the game scene again to show the next level:
var summaryScene = game.summaryScene = Object.create(scene); summaryScene.node = document.getElementById('summary-scene'); summaryScene.handleInput = function() { document.getElementById('next-level-button').onclick = function() { game.flow.nextLevel(); }; };
At last, we add the game over scene code to the
scenes.js
file. When the game is over, we bring the player back to the menu scene after the back button is clicked:var gameoverScene = game.gameoverScene = Object.create(scene); gameoverScene.node = document.getElementById('gameover-scene'); gameoverScene.handleInput = function() { var scene = this; document.getElementById('back-to-menu-button').onclick = function() { game.flow.startOver(); }; };
Now, we will define a game flow in the
game.js
file that will help us control how to show and hide the scenes:// Main Game Logic game.flow = { startOver: function() { game.startScene.hide(); game.summaryScene.hide(); game.gameoverScene.hide(); game.gameScene.hide(); game.startScene.show(); }, gameWin: function() { game.gameScene.hide(); game.summaryScene.show(); }, gameOver: function() { game.startScene.show(); game.gameScene.hide(); game.gameoverScene.show(); }, nextLevel: function() { game.startScene.hide(); game.summaryScene.hide(); game.gameScene.show(); }, finishLevel: function() { game.gameScene.hide(); game.summaryScene.show(); }, }
The
init
function is the entry point of the game. Inside this function, we will register the click input listeners:var init = function() { game.startScene.handleInput(); game.summaryScene.handleInput(); game.gameoverScene.handleInput(); game.gameScene.handleInput(); }
At last, we need some styling for the scenes to make them work. Put the following CSS rules at the end of the
game.css
file:#game { width: 480px; height: 600px; margin: 0 auto; border: 1px solid #333; text-align: center; position: relative; overflow: hidden; } .scene { background: white; width: 100%; height: 100%; position: absolute; transition: all .4s ease-out; } .scene.out {top: -150%;} .scene.in {top: 0;} .button { width: 145px; height: 39px; display: block; margin: auto; text-indent: 120%; white-space: nowrap; overflow: hidden; background-repeat: no-repeat; } .button:hover { background-position: 0 -39px; } .button:active { background-position: 0 0; } #start-scene {background: url(images/menu_bg.png);} #start-btn { background-image: url(images/start_btn.png); margin-top: 270px; } #game-scene {background: url(images/game_bg.png);} #game-scene.out { opacity: 0; top: 0; transition-delay: .5s; } #summary-scene {background: url(images/summary_bg.png);} next-level-button { background-image: url(images/next_btn.png); margin-top: 370px; } #summary-scene.in { transition-delay: .5s; } #gameover-scene { background: url(images/gameover_bg.png); } #back-to-menu-button { background-image: url(images/restart_btn.png); margin-top: 270px; }
We have created scenes in this task. Now let's take a closer look at each block of code to see how they work together.
Each button is of 145 pixels by 39 pixels in size and has two states: a normal and a hover state. Both states are combined in one image and thus the final image is of 78 pixels in height. The bottom part contains the hover state. We switch these states by setting the background's y position to 0
pixel for the normal state and -39
pixel for the hover state, as shown in the following screenshot:

We encapsulated the scene's management code in a file named scene.js
. Similar to the game.js
file, we start every logic file with a self-invoked anonymous function. Inside the function, we use the same namespace: colorQuestGame
.
We use CSS to control the visibility of the scenes. The .in
and .out
CSS properties that apply to all the scenes have different top values. One is -150%
to ensure it is not visible, and the other is top: 0
; therefore, it is visible in the game element.
Then, we toggle each scene between the .in
and .out
class to control the visibility. In addition, we add transition to these CSS rules so changing the value from 0 to -150 percent and vice-versa becomes a smooth animation.
There are four scenes in this game: the pregame start scene, game scene, game over scene, and level-completed summary scene. Each scene shares certain common logic; this includes showing and hiding themselves. In JavaScript, we can use object inheritance to share common logic. The scene is an object with default show-and-hide behaviors. It also defines a dummy sceneElement
property that points to the DOM element of that scene.
A game scene is another object. However, we do not create it as a normal object. We use the Object.create
method with this scene as the argument. The Object.create
method will chain the argument as the new object's prototype. This is known as a prototype chain.
What if we want to show a different effect for hiding a game scene? It depends on whether your effects are done in CSS or JavaScript. If it is a CSS-only effect, you can just add rules to the #game-scene.out
scene. In the management part of the scene, .in
is to display the scene and .out
is for rules that hide the scene.
For the CSS approach, assume that we want a fade-out effect; we can do so using the following CSS rule:
#game-scene.out { opacity: 0; top: 0; }
JavaScript is an object-oriented programming language without the requirement of a class definition. It uses a prototype chain for object inheritance. Each object comes with a special prototype
property. The prototype defines what this object is based on; you can think of it as inheritance in traditional object-oriented languages.
Let's take a look at how JavaScript accesses object properties. When we call Scene.show()
, it takes a look at the property list and finds a property named show
, which is of the type function
.
Imagine now that the show
method is not there. The JavaScript runtime looks it up in the prototype. If it is found inside the prototype, it returns the method. Otherwise, it keeps looking up prototype's prototype until it reaches a generic object.
This is the meaning of prototype chaining. We build an object based on another object by putting the other object into the prototype. The following screenshot shows the startScene
property and the properties in its prototype (scene
):

In order to attach the object to prototype, we use Object.create
. The original object's prototype is attached to the new object. It allows us to directly inherit an object's instance into a new object without going through the traditional abstract class definition.
Another approach to put a different object into the prototype is using the Function
and new
approaches. When we define a function, we can use new
to create an object from the function. For example, observe the following function:
function Scene() {} Scene.prototype.show = function() {} Scene.prototype.hide = function() {}
Now, when we create a scene object instance by using new Scene(),
the instance has the method show
and hide
. If we want to have a GameScene
definition based on the Scene
, we can do that:
function GameScene() {} GameScene.prototype = new Scene();
In this function, we have added an instance of Scene
into GameScene
. Therefore, the GameScene
instance now has all the functionalities of Scene
.
Note
More explanation on the difference between the new instance approach and the Object.create
approach can be found in the following link to a post from the Mozilla Developer Network:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
Besides changing the CSS .in
and .out
classes of the scenes, we can also add extra animation or logic when the scene shows and hides. Instead of the plain scene movement, we can further enhance the transition by defining the game objects in
and out
of the CSS rule. For example, we can make the buttons fade out during the transition, or we can drop the quest element to the bottom to create an illusion of it being unlocked; this can be done using the following code:
// an example of custom hide function gameScene.hide = function() { // invoke the hide function inside the prototype chain. // (aka. super.hide()) Object.getPrototypeOf(this).hide.call(this); /* extra */ // add the class for the out effect var questView = document.getElementById('quest'); questView.classList.add('out'); /* end extra */ }
Since, we have overridden the hide
method from the scene object, we need to call the prototype's hide using the scope of gameScene
. Then, we add our extra logic to add the out
class to the quest DOM element.
We define the dropping effect with CSS transform and transition:
#quest.out { transition: all .8s ease-out; transform: translateY(800px) rotate(50deg); }
The out
object of the game scene is a delayed fading out transition:
#game-scene.out, #summary-scene.in { transition-delay: .5s; }
In addition, we used the transition delay to make sure that the drop animation is displayed before the scene goes out and the next scene goes in.
Tip
Some new properties of CSS are not stable yet. When a vendor adds support to these styles, they add a vendor-prefix to the property. The vendor prefix indicates that the user should use that property with caution.
In this project, we will omit all the vendor-prefixes for clarity when showing the code in the book. In order to make sure that the code works in a browser, we may need to add the following vendor prefixes: -
webkit-
(for Chrome and Safari), -moz
-
(for Mozilla Firefox), -
o-
(for Opera), and -ms
-
(for Internet Explorer).
If you find adding prefixes troublesome, you may use some tools for help. The tool prefix-free (http://leaverou.github.io/prefixfree/) is one that can help. Compile tools will add these prefixes for you, such as CSS preprocess compilers.
In this task, we declare the quest level and then display the level in the quest composition view.
We will need three more JavaScript files in this task, so let's create them. These files are as follows:
At the end of this task, we should be able to create a composition of patterns according to our level. For example, the following quest composition is composed of four patterns: a circle, the left and right trapezoid, and lines, as shown in the following screenshot:

We put patterns into composition using the following steps:
To start with, in HTML, we want to remove the dummy
gameover
link and the finish link from the game scene. As we are now putting real content in the game scene, these two links may affect the placement of our game elements:<div id="game-scene" class="scene out"> <div id="stage">Stage 1</div> <div id='quest' class="quest"> <div id="quest-composition" class="composition"></div> </div> </div> <div id='element-template'> <!-- for composition view --> <div class="pattern" data-pattern='1'></div> </div>
We will import the three files at the end of HTML file. They are
patch.js
,composition.js
, andquest.js
:<script src='js/patch.js'></script> <script src='js/composition.js'></script> <script src='js/quest.js'></script> <script src='js/scenes.js'></script> <script src='js/game.js'></script>
We want to make it easy to remove all the DOM elements from the quest view. This is why we have a patch file. Put the following code in the
patch.js
file to add theremoveAllChildren
method to all the DOM nodes:// add removeAllChildren to Node object. Node.prototype.removeAllChildren = function() { while(this.firstChild) { this.removeChild(this.firstChild); } };
Then, we add a basic skeleton to the
composition.js
file:(function(){ var game = this.colorQuestGame = this.colorQuestGame || {}; // composition model definition // composition is a deck of pattern put together var Composition = game.Composition = (function(){ function Composition(){ this.data = []; } return Composition; })(); })();
In the
quest.js
file, we represent the level of data in arrays. The number is the pattern. We will discuss how we come up with this special array structure later:(function(){ var game = this.colorQuestGame = this.colorQuestGame || {}; // level data var questLevels = [ [ [5, 6], [3] ], [ [6], [1, 2]], [ [5, 6] ], [ [3], [1, 2], [4] ], [ [1, 2], [3], [4], [5, 6]], ]; // quest model definition // quest is a kind of composition, the difference is that quest is specifically used as the question for player to give the answer. // so it comes with comparing logic. var Quest = game.Quest = (function(){ function Quest(level){ var questData = questLevels[level]; this.data = questData; } Quest.prototype = new game.Composition(); // extends the Quest prototype from Composition. return Quest; })(); })();
Since we have removed the dummy
gameover
and have finished with the link of the game scene, now, from thescenes.js
file, we will also remove theonclick
event for these two links inside thehandleInput
method.We add a new method to the
gameScene
instance that displays the data in the game scene. This method creates the patterns in the quest area of the game according to the given data:gameScene.visualize = function(quest) { var questData = quest.data; var patternsToShow = []; for (var i in questData) { for (var j in questData[i]) { patternsToShow.push(questData[i][j]); } } // Quest // visualize the quest composition view: var questCompositionNode = document.getElementById('quest-composition'); // empty the DOM view questCompositionNode.removeAllChildren(); // visualize the pattern view: for (var i in patternsToShow) { var patternNode = document.querySelector('#element-template .pattern').cloneNode(/*clone children=*/true); patternNode.setAttribute('data-pattern', patternsToShow[i]); questCompositionNode.appendChild(patternNode); } };
We need to modify the game flow in the
game.js
file to show the quest:game.flow = { currentLevel: 3, startLevel: function() { game.quest = new game.Quest(this.currentLevel); game.gameScene.visualize(game.quest); }, ... } var init = function() { ... game.flow.startLevel(); }
Finally, we will create the view of the patterns and the quest in CSS:
#stage { position: absolute; width: 100%; height: 30px; line-height: 30px; } /* Template */ #element-template {display: none;} /* quest */ #quest { width: 200px; height: 200px; position: absolute; left: 30px; top: 70px; } /* individual pattern */ .pattern { width: 200px; height: 200px; background-size: contain; } .pattern[data-pattern='1'] { background-image: url(images/pattern1.png); } /* pattern 2-5 puts here */ .pattern[data-pattern='6'] { background-image: url(images/pattern6.png); } .composition { position: relative; height: 200px; background: white; } .composition > .pattern { position: absolute; }
We created the visual and data composition of a quest. Let's go in detail on what we have done in this task.
While designing games, we usually want to separate the logic of manipulating data and the logic of displaying and drawing elements. In our logic, the composition and quest are data. The scenes are for displaying. That's why we use the gameScene.visualize
method to display data into the DOM element once we declare the quest composition data.
We need to dynamically create elements to represent the pattern in the quest DOM node. Sometimes we create HTML directly in JavaScript and append it to the node. A better approach is to have the HTML placed inside, well, HTML. That's why we have the template element for JavaScript to clone it and put it back into the quest node.
Note
Using the data-* attribute
It is often useful to use the data-*
attribute to embed extra information when we use DOM elements to represent game objects. Take the card as an instance. We can define data-pattern='3'
to indicate that element is a visual of pattern number 3. We can define whatever we like as long as it begins with data-
. Then, we can use the getAttribute
method to access it and use the setAttribute
method to update it. Alternatively, we can use the dataset
method to access the data-*
attribute.
A pattern is a stack of background-transparent cards. We can represent each card as a DIV and overlay them together in one container. In the composition node, we overlap the pattern by setting the position to absolute, top-left position to 0.
Whenever we use absolute elements, we want to make sure that we have control of the reference point of the top and left properties; this means controlling where the top 0 and left 0 positions are.
Elements that are positioned at the absolute point reference the top-left point in the following way:
They find the first parent with a position and set it as absolute or relative
They use the body's top-left point if no parents are found with the position's setting
Therefore, what we need to do is set a position that is relative to the container, namely, .composition
.
The position styling for the quest and pattern has been defined in the CSS. What we need to do is append the newly created HTML node to the quest node from the gameScene.visualize
method. The pattern HTML nodes are created from the template and with the class defined that match the CSS rules.
In this game, we require the player to select the pattern in the correct sequence to match the quest. However, some patterns are not overlapped with other patterns. In this case, we will put the two non-overlapped pairs together so that the order of choosing among these patterns will not be treated in the wrong order.
We would like to come up with an approach to compare the player's composition with the quest's composition.
The quest is composited by a sequence of patterns. A straightforward approach is to store the pattern sequence in an array. Then, all we need to do is compare whether the player's sequence is exactly the same as the given one.
Sounds good, but it fails in one case. In our patterns, there are some patterns that don't overlap with the others. Take the following pattern shown in the screenshot as an example:

The trapezoids to the left and right fit together without overlapping. We require the player to match the pattern visually so the sequence of these two selections does not change the effect, as shown in the following screenshot:

However, in the following pattern, the circle does overlap with the triangle:

Therefore, a simple sequence array does not work. Let's improve how we store the patterns in an array. How about using a 2-dimensional array?
The first dimension is the z index of the patterns, which is the sequence that players must match.
In order to represent the same level of relationship between the two trapezoids, we put them into one slot inside our array; thus, the array inside the code becomes the following:
[ [Left Trapezoid, Right Trapezoid], [Triangle] ]
To make the array item type consistent, we can force a single item to be wrapped inside the group too. Let's call each group of non-overlapped pattern layer.
Now, the final data array will be as follows:
[ [Left Trapezoid, Right Trapezoid], [Triangle] ]
Here, each array inside the quest level represents one layer with patterns that are interchangeable. Any overlapping pattern will be another new layer. Moreover, we put all the levels into an array to represent all the levels.
The composition module contains only a data instance variable in the current task. The data has the same format as the multilayered patterns. Quest modules inherit the composition with logic that is related to the quest levels.
In future tasks, we are going to add more logic to the composition and quest modules to help in manipulating the format of a multilayer pattern.
In this task, we are going to list the pattern in the deck. Later, we will let the player select patterns from this deck.
We are going to need a new module to handle the display of the composition. Let's create a new empty JavaScript file named composition-view.js
.
We need to import the file into the index.html
file, as follows:
<script src='js/composition-view.js'></script>
Let's work on the pattern with the following steps:
In the game scene of the
index.html
file, we add two DOM elements, namely,#your-composition
and#deck
:<div id="game-scene" class="scene out"> ... <div id="your-composition"></div> <div id="deck" class="deck"></div> </div>
In the template element, we add the template for the pattern's slot:
<div id="element-template"> <!-- for deck view --> <div class="pattern-slot"> <div class="pattern" data-pattern="1"></div> </div> ... </div>
The following is our
composition-view.js
file:(function(){ var game = this.colorQuestGame = this.colorQuestGame || {}; // composition module game.compositionView = { node: document.getElementById('your-composition'), }; })();
Before the end of the
gameScene.visualize
method, we add the visualization logic for the player's composition:// randomize the patterns array patternsToShow.sort(function(a, b){ return Math.random() - 0.5; }); // empty the current deck view var deckNode = document.getElementById('deck'); deckNode.removeAllChildren(); // add the pattern to the deck view for (var i in patternsToShow) { var patternSlotNode = document.querySelector('#element-template .pattern-slot').cloneNode(/*clone children=*/true); patternSlotNode.querySelector('.pattern').setAttribute('data-pattern', patternsToShow[i]); deckNode.appendChild(patternSlotNode); }
From the
game.js
file, we remove all the selected patterns before starting a new level:nextLevel: function() { ... game.compositionView.node.removeAllChildren(); this.startLevel(); },
We need the following CSS style for the composition and patterns:
/* player's composition and pattern */ #your-composition { position: absolute; width: 100px; height: 100px; right: 65px; top: 120px; border: 3px solid #999; } #your-composition > .pattern { width: 100px; height: 100px; position: absolute; } /* deck and pattern */ .deck { position: absolute; top: 360px; left: 20px; } .pattern-slot { width: 100px; height: 100px; outline: 4px solid #BC7702; float: left; border-radius: 3px; margin: 10px 0 0 10px; } .deck .pattern { width: 100%; height: 100%; }
We should have the following screenshot once this task is completed. The deck shows the patterns that a player needs to compose the quest:

JavaScript comes with a sort
method for the array. Normally, we compare the given two array elements and return +1
or -1
to control elements' swapping.
We can randomize an array by randomly returning either +1
or -1
:
patternsToShow.sort(function(a, b){ return Math.random() - 0.5; });
After we randomize the patterns, we clone each pattern from the template and append them to the deck element.
In this task, we allow players to select the pattern from their decks and display the sequence of the selection in the composition view.
Perform the following steps to add user interaction to our game:
We allow players to undo their selection, so we need to add an undo button to the
index.html
file:<a href="#" id="undo-button" class="button">Undo</a>
When starting a level in the
game.js
file, we store the player's selection sequence and register the clicking event by adding the following highlighted code:startLevel: function() { game.quest = new game.Quest(this.currentLevel); game.compositionSeq = []; game.composition = new game.Composition(); game.gameScene.visualize(game.quest); game.gameScene.handleInput(); },
In the
patch.js
file, we need to addforEach
to theNodeList
andHTMLCollection
objects using the following code:NodeList.prototype.forEach = Array.prototype.forEach; HTMLCollection.prototype.forEach = Array.prototype.forEach;
In the
composition-view.js
file, we need the following methods to display the pattern selection in the composition's DOM element:game.compositionView = { node: document.getElementById('your-composition'), pushPattern: function(patternId) { var newChild = document.createElement('div'); newChild.classList.add('pattern'); newChild.setAttribute('data-pattern', patternId); this.node.appendChild(newChild); }, pullPattern: function() { var lastChild = this.node.querySelector('.pattern:last-child'); if (lastChild) { // find the pattern in the deck and make it visible var deckNode = document.getElementById('deck'); var resumePattern = deckNode.querySelector('[data-pattern="' + lastChild.getAttribute('data-pattern') + '"]'); resumePattern.style.display = 'block'; // remove the current pattern this.node.removeChild(lastChild); } }, selectPattern: function(pattern) { this.pushPattern(pattern); game.compositionSeq.push(pattern); }, undo: function() { this.pullPattern(); game.compositionSeq.pop(); }, };
Then, we need the mouse event to invoke our selection logic. In the
scenes.js
file, we add the following clicking event to thegameScene
:gameScene.handleInput = function() { document.querySelectorAll("#deck .pattern").forEach(function(elm){ elm.onclick= function(){ var pattern = elm.getAttribute('data-pattern'); elm.style.display = 'none'; game.compositionView.selectPattern(pattern); }; }); var undoBtn = document.getElementById('undo-button'); undoBtn.onclick = function(e){ game.compositionView.undo(); e.preventDefault(); }; };
Let's move to styling. We have a new undo button, so we need the following CSS rules to place it in the right position with the image:
#undo-button { position: absolute; right: 70px; top: 240px; z-index: 999; background: url(images/undo_btn.png) no-repeat; width: 90px; height: 26px; } #undo-button:hover {background-position: 0 -26px;}
Also, we add mouse-related styling to the pattern's slot:
.pattern-slot:hover{outline-color: #D68700;} .pattern-slot:active {outline-color: #BC7702;}
The selection is done by the click event on the pattern. Basically, we get the pattern ID from the data-
attribute. Once the pattern ID is known, it triggers the following method:
game.compositionView.selectPattern(pattern);
Then, the composition pushes the selection into an array.
In this task, we create logic to make multilayered pattern compositions from player's selection and then compare it with the quest pattern's composition.
We need a way to represent the pattern overlapping and non-overlapping relationships.
We will use the data structure of this section to remember the overlapping relationship. Most patterns will overlap others, so we would need to think the other way round. So, we store those patterns that do not overlap together.
The pattern is a two-dimensional (2D) array. The first dimension contains every pattern. Each pattern is a list of the other patterns that do not overlay with it.
For example, the following A
pattern does not overlap with the C
and D
shapes. We represent it with the ensuing equation:
array['A'] = [ 'C', 'D'];
For a pattern that always overlaps with the others, an empty array will be assigned.
In the following steps, we code the logic that allows us to compare two given quest compositions:
In the
composition.js
file, we have the following class variable to represent the relationship of the overlapping patterns. It indicates the pattern that overlaps with other patterns. The index of thenonOverlappedPattern
array is the pattern ID, and the corresponding array value is the list of patterns that do not overlap with that pattern:// static variable. available as only one copy among all composition instances. Composition.nonOverlappedPattern = [ [], // pattern 0 [2], // pattern 1, doesn't overlap with pattern 2. [1], // pattern 2, doesn't overlap with pattern 1. [], // pattern 3 [], // pattern 4 [6], // pattern 5, doesn't overlap with pattern 6. [5], // pattern 6, doesn't overlap with pattern 5. ];
We create the following new method in the
Composition
method that can turn a composition back to a one-dimension array:Composition.prototype.toSequence = function() { var seq = []; for (var i=0; i < this.data.length; i++) { for (var j=0; j <this.data[i].length; j++ ) { seq.push(this.data[i][j]); } } return seq; }
Then, we create the
createFromSequence
method with the following code that turns a sequence back to a composition:Composition.createFromSequence = function(sequence) { // function to determine if 2 given patterns overlap. var allowPatternsInSameLevel = function(patternA, patternB) { // iterate the array to see if current pattern overlaps the var nonOverlappedPattern = Composition.nonOverlappedPattern[patternA] var len = nonOverlappedPattern.length; for (var i=0; i<len; i++) { if (nonOverlappedPattern[i] === parseInt(patternB)) { return true; } } return false; }; // layer is an array that contains existing pattern var layerAllowsPattern = function(layer, pattern) { for (var i=0, len=layer.length; i<len; i++) { if (!allowPatternsInSameLevel(layer[i], pattern)) { return false; } } return true; }; // end helper functions var newComposition = new Composition(); var layer = []; for (var i=0, len=sequence.length; i<len; i++) { if (layerAllowsPattern(layer, sequence[i])) { // we are still in same layer. layer.push(sequence[i]); } else { // two patterns overlapped, // we push the current layer to composition // and use a new layer for the current pattern. newComposition.data.push(layer); layer = []; // new array instance to prevent browser using the same array and crashes the data. layer.push(sequence[i]); } } // for the last layer if (layer.length> 0) newComposition.data.push(layer); return newComposition; }
We add a new method to the
Quest
method that can compare two multilayered pattern compositions and check whether they are equal to each other:Quest.prototype.isEqualToComposition = function(composition) { var a = this.data; var b = composition.data; // sort each level in both array for (var i=0, len=a.length; i<len; i++) { a[i].sort(); } for (var i=0, len=b.length; i<len; i++) { b[i].sort(); } // flatten both compositions into sequence. a = this.toSequence(); b = composition.toSequence(); if (a.length !== b.length) return false; for (var i=0, len=a.length; i<len; i++) { if (parseInt(a[i]) !== parseInt(b[i])) return false; } return true; }
In the
composition-view.js
file, we check whether the player's latest selection matches the quest level. Therefore, in both theselectPattern
andundo
methods, we keep a composition from the sequence and check it with the quest level:selectPattern: function(pattern) { ... game.composition = game.Composition.createFromSequence(game.compositionSeq); if (game.quest.isEqualToComposition(game.composition)){ game.flow.gameWin(); } }, undo: function() { ... game.composition = game.Composition.createFromSequence(game.compositionSeq); if (game.quest.isEqualToComposition(game.composition)) { game.flow.gameWin(); } },
The comparison requires us to create a multilayered pattern composition from the selection sequence. Then, we compare the player's one with that of the quest level.
Composition is a two-dimensional array that we need to compare between the quest and the player's selection. However, the player's selection is a sequence that does not reflect our layer's structure. This is the reason why we need a conversion between the composition and the sequence. Moreover, we need to keep a copy of the player's sequence because we need that information for the undo feature.
In our createFromSequence
method, we created two helper functions to manipulate multilayered patterns. The allowPatternsInSameLevel
function checks whether the given two patterns can be overlapped together by looking up the non-overlapped pattern table. The layerAllowsPattern
function makes use of the previous function and checks whether the given pattern fits all other patterns inside the given layer, which is an array of all non-overlapped patterns put together.
The createFromSequence
method makes use of the function to scan the selection array in a sequence and then put each pattern into either the same layer (without overlapping) or a new layer.
Finally, we have the isEqualToComposition
method in the quest instance to compare the player's selection composition with the quest level's one. This method sorts all the layers in both the compositions into the same order and then converts the composition back to a one-dimension array using the toSequence
method.
This ensures the player's selection can match our defined level of data on non-overlapping patterns, regardless of the order of selection.
In this task, we are going to show how to advance to the next level when the player's composition matches that of the quest compositions in the current level.
This time, we need quest-level data in the quest.js
file to be accessible from another file, so we need to attach the questLevels
and questData
methods to the game scope rather than the original local scope:
In the
quest.js
file, we change thequestLevels
declaration fromvar questLevels
togame.questLevels
.We apply the same to
questData
:// from var questData = questLevels[level]; // to var questData = game.questLevels[level];
In the
scenes.js
file, we display the level with the following function:gameScene.updateLevelInfo = function(level) { document.getElementById('stage').textContent = "Stage "+ level; };
At last, we modify the game flow in the
game.js
file to count the level:game.flow = { currentLevel: -1, maxLevel: game.questLevels.length - 1, startOver: function() { ... this.currentLevel = -1; }, nextLevel: function() { this.currentLevel+=1; if (this.currentLevel>= this.maxLevel) this.currentLevel = this.maxLevel; game.gameScene.updateLevelInfo(this.currentLevel+1); // when displaying level, we start from 1 instead of 0, so +1 here. ... }, ... }
The level up is done by counting the current level and selecting the level from the questLevels
array. The nextLevel
method is used to increase the currentLevel
counter. Moreover, this method is called once startButton
is clicked from the menu. Therefore, it will increase the currentLevel
counter at the beginning. This is why we set the initial value of currentLevel
to -1
. This will ensure that it selects the first level once the game starts that is at index 0 of the level array. We reset currentLevel
in the startOver
method once the game is over. On the other hand, we display the current level at the top of the game inside the #stage
element. Therefore, we also update that wording every time the level is up.
In this task, we are going to give a final touch to the game by adding a countdown timer. With the timer, the players are not only challenged to select the correct sequence but also restricted in terms of time in order to make the game more exciting for the player.
We will need a new file for the timer. Let's create a new empty file and name it timer.js
. With the new file, we also need to import it in the index.html
file, which is done using the following code:
<script src='js/timer.js'></script>
The timer would be a horizontal bar at the top of the game. Its width decreases when you count down the time.

Let's count down the game with the following steps:
In the
index.html
file, we need to add adiv
method for the timer. Add the following code to the HTML inside#game
:<div id="timer"></div>
Let's move to the
game.js
file to add the timer control to the game flow:gameWin: function() { ... game.timer.stop(); }, gameOver: function() { ... game.timer.stop(); }, startLevel: function() { ... game.timer.restart(); },
We remove the following code from the
init
method in order to prevent the timer from starting during the menu scene:var init = function() { game.flow.startLevel(); <<<<< Delete this line. }
The timer is something that can be reused in other games, so it is worth creating a dedicated file named
timer.js
. Let's put the following timer code into the newly createdtimer.js
file:(function(){ var game = this.colorQuestGame = this.colorQuestGame || {}; game.timer = { interval: undefined, countFrom: 60, // second count: this.countFrom, progressView: document.getElementById('timer'), restart: function() { if (this.interval) { clearInterval(this.interval); } this.count = this.countFrom; this.interval = setInterval((this.tick).bind(this), 1000); }, stop: function() { clearInterval(this.interval); }, tick: function() { this.count -= 1; if (this.count<= 0) { this.count = 0; clearInterval(this.interval); game.flow.gameOver(); } // update the view var progress = this.count / this.countFrom * 100; this.progressView.style.width = progress + "%"; } } })();
Now, it is time for styling. Let's append the following CSS rules to the
game.css
file:#timer { position: absolute; top: 0; left: 0; height: 30px; width: 100%; background: #7ADAF6 url(images/timer_symbol.png) no- repeat 5px 50%; border-bottom: 1px solid #4F8EA1; transition: all .3s ease-out; }
A simple way to count down is to use the setInterval
or setTimeout
methods. However, for games, we usually want a global timer to act as the world clock. This way, all the elements will listen for the tick of the timer. When the world clock pauses, all game elements stop, when it resumes, all resume. That's why we have a separate timer object.
The timer has its own interval managed. All we need to do is start and stop the timer and it will run the setInterval
and clearInterval
methods for us.
By default, the tick function checks whether the time is up and also updates the visual timer DOM node with a width that reflects the countdown.
We created our first playable game. We learned how to separate data manipulation and visual presentation. We also learned how to manage the game scenes and create logic for each state.
There are many ways to create games with web standard. We explored how to create the game with HTML DOM elements, CSS properties, and JavaScript. There are other approaches such as using canvas-drawing APIs and even with some third-party game libraries. In later projects, we will explore these different approaches.
There are some approaches though, such as WebGL, which we will not discuss in this book. The programming of WebGL requires a different kind of programming skills that is not quite related to traditional web designing skills.
In the next project, we will create a card game that uses flipping and CSS animation techniques.
There are always improvements in game development. The following sections act as suggestions that can further enhance the game.
Currently, the game restarts every time a page is refreshed. We can keep progress by saving the completed level locally. Furthermore, we can store the current level that is in progress so a player resumes at the exact point they left the game. We are going to discuss local storage in Project 5, Building an Isometric City Game, when creating the isometric city-building game.
A level-unlocking game usually works better with stars collection. In each quest level, we can define three levels of achievement. Each achievement rewards the player with one star. For example, completion of a quest can be worth the first star, no-undo is worth the second star, and the final star is awarded only when a player completes the level very fast.
Then, we may have a total count of all the collected stars, and it can be used to unlock new quests or even unlocking new abilities to help player advances.