HTML code : Image Puzzle Game

In this section I am going to create a image-puzzle game by using HTML5 Canvas and Javascript. You can learn HTML from our HTML Tutorial.This will provide a medium level image puzzle that works with any given image, and you can change its difficulty levels by changing in code youself.

You must have the basic knowledge of html language to understand the given program. Please see our HTML Tutorial.

Click the Button to See Demo
image_puzzle image_puzzle

HTML code to create Image Puzzle Game

<!DOCTYPE html>
<html>
<head>
<script>
const DIFFICULTY = 3;
const HOVER_TINT = '#009900';
var _stage;
var _canvas;
var _loaded_image;
var _pieces;
var _puzzleWidth;
var _puzzleHeight;
var _pieceWidth;
var _pieceHeight;
var _currentPiece;
var _currentDropPiece;

var _mouse;
function init(){
_loaded_image = new Image();
_loaded_image.addEventListener('load',onImage,false);
_loaded_image.src = "Dora_Hexa_Puzzle.jpg";
}
function onImage(e){
_pieceWidth = Math.floor(_loaded_image.width / DIFFICULTY)
_pieceHeight = Math.floor(_loaded_image.height / DIFFICULTY)
_puzzleWidth = _pieceWidth * DIFFICULTY;
_puzzleHeight = _pieceHeight * DIFFICULTY;
setCanvas();
initPuzzle();
}
function setCanvas(){
_canvas = document.getElementById('canvas');
_stage = _canvas.getContext('2d');
_canvas.width = _puzzleWidth;
_canvas.height = _puzzleHeight;
_canvas.style.border = "1px solid cyan";
}
function initPuzzle(){
_pieces = [];
_mouse = {x:0,y:0};
_currentPiece = null;
_currentDropPiece = null;
_stage.drawImage(_loaded_image, 0, 0, _puzzleWidth, _puzzleHeight, 0, 0, _puzzleWidth, _puzzleHeight);
createTitle("Click the Image to Start Puzzle");
buildPieces();
}
function createTitle(msg){
_stage.fillStyle = "#000000";
_stage.globalAlpha = .4;
_stage.fillRect(100,_puzzleHeight - 40,_puzzleWidth - 200,40);
_stage.fillStyle = "#FFFFFF";
_stage.globalAlpha = 1;
_stage.textAlign = "center";
_stage.textBaseline = "middle";
_stage.font = "20px Arial";
_stage.fillText(msg,_puzzleWidth / 2,_puzzleHeight - 20);
}
function buildPieces(){
var i;
var piece;
var xPos = 0;
var yPos = 0;
for(i = 0;i < DIFFICULTY * DIFFICULTY;i++){
piece = {};
piece.sx = xPos;
piece.sy = yPos;
_pieces.push(piece);
xPos += _pieceWidth;
if(xPos >= _puzzleWidth){
xPos = 0;
yPos += _pieceHeight;
}
}
document.onmousedown = shufflePuzzle;
}
function shufflePuzzle(){
_pieces = shuffleArray(_pieces);
_stage.clearRect(0,0,_puzzleWidth,_puzzleHeight);
var i;
var piece;
var xPos = 0;
var yPos = 0;
for(i = 0;i < _pieces.length;i++){
piece = _pieces[i];
piece.xPos = xPos;
piece.yPos = yPos;
_stage.drawImage(_loaded_image, piece.sx, piece.sy, _pieceWidth, _pieceHeight, xPos, yPos, _pieceWidth, _pieceHeight);
_stage.strokeRect(xPos, yPos, _pieceWidth,_pieceHeight);
xPos += _pieceWidth;
if(xPos >= _puzzleWidth){
xPos = 0;
yPos += _pieceHeight;
}
}
document.onmousedown = onPuzzleClick;
}
function onPuzzleClick(e){
if(e.layerX || e.layerX == 0){
_mouse.x = e.layerX - _canvas.offsetLeft;
_mouse.y = e.layerY - _canvas.offsetTop;
}
else if(e.offsetX || e.offsetX == 0){
_mouse.x = e.offsetX - _canvas.offsetLeft;
_mouse.y = e.offsetY - _canvas.offsetTop;
}
_currentPiece = checkPieceClicked();
if(_currentPiece != null){
_stage.clearRect(_currentPiece.xPos,_currentPiece.yPos,_pieceWidth,_pieceHeight);
_stage.save();
_stage.globalAlpha = .9;
_stage.drawImage(_loaded_image, _currentPiece.sx, _currentPiece.sy, _pieceWidth, _pieceHeight, _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight);
_stage.restore();
document.onmousemove = updatePuzzle;
document.onmouseup = pieceDropped;
}
}
function checkPieceClicked(){
var i;
var piece;
for(i = 0;i < _pieces.length;i++){
piece = _pieces[i];
if(_mouse.x < piece.xPos || _mouse.x > (piece.xPos + _pieceWidth) || _mouse.y < piece.yPos || _mouse.y > (piece.yPos + _pieceHeight)){
//PIECE NOT HIT
}
else{
return piece;
}
}
return null;
}
function updatePuzzle(e){
_currentDropPiece = null;
if(e.layerX || e.layerX == 0){
_mouse.x = e.layerX - _canvas.offsetLeft;
_mouse.y = e.layerY - _canvas.offsetTop;
}
else if(e.offsetX || e.offsetX == 0){
_mouse.x = e.offsetX - _canvas.offsetLeft;
_mouse.y = e.offsetY - _canvas.offsetTop;
}
_stage.clearRect(0,0,_puzzleWidth,_puzzleHeight);
var i;
var piece;
for(i = 0;i < _pieces.length;i++){
piece = _pieces[i];
if(piece == _currentPiece){
continue;
}
_stage.drawImage(_loaded_image, piece.sx, piece.sy, _pieceWidth, _pieceHeight, piece.xPos, piece.yPos, _pieceWidth, _pieceHeight);
_stage.strokeRect(piece.xPos, piece.yPos, _pieceWidth,_pieceHeight);
if(_currentDropPiece == null){
if(_mouse.x < piece.xPos || _mouse.x > (piece.xPos + _pieceWidth) || _mouse.y < piece.yPos || _mouse.y > (piece.yPos + _pieceHeight)){
//NOT OVER
}
else{
_currentDropPiece = piece;
_stage.save();
_stage.globalAlpha = .4;
_stage.fillStyle = HOVER_TINT;
_stage.fillRect(_currentDropPiece.xPos,_currentDropPiece.yPos,_pieceWidth, _pieceHeight);
_stage.restore();
}
}
}
_stage.save();
_stage.globalAlpha = .6;
_stage.drawImage(_loaded_image, _currentPiece.sx, _currentPiece.sy, _pieceWidth, _pieceHeight, _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight);
_stage.restore();
_stage.strokeRect( _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth,_pieceHeight);
}
function pieceDropped(e){
document.onmousemove = null;
document.onmouseup = null;
if(_currentDropPiece != null){
var tmp = {xPos:_currentPiece.xPos,yPos:_currentPiece.yPos};
_currentPiece.xPos = _currentDropPiece.xPos;
_currentPiece.yPos = _currentDropPiece.yPos;
_currentDropPiece.xPos = tmp.xPos;
_currentDropPiece.yPos = tmp.yPos;
} resetPuzzleAndCheckWin();
}
function resetPuzzleAndCheckWin(){
_stage.clearRect(0,0,_puzzleWidth,_puzzleHeight);
var gameWin = true;
var i;
var piece;
for(i = 0;i < _pieces.length;i++){
piece = _pieces[i];
_stage.drawImage(_loaded_image, piece.sx, piece.sy, _pieceWidth, _pieceHeight, piece.xPos, piece.yPos, _pieceWidth, _pieceHeight);
_stage.strokeRect(piece.xPos, piece.yPos, _pieceWidth,_pieceHeight);
if(piece.xPos != piece.sx || piece.yPos != piece.sy){
gameWin = false;
}
}
if(gameWin){
setTimeout(gameOver,500);
}
}
function gameOver(){
document.onmousedown = null;
document.onmousemove = null;
document.onmouseup = null;
initPuzzle();
}
function shuffleArray(o){
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}
</script>
</head>
<body onload="init();">
<center>
<br><br><br><br><br><br><br><br>
<canvas id="canvas"></canvas></center>
</body>
</html>
Click the Button to See Demo


Code Explanation

  • As I already told you that the given program consist of two sections HTML and Javascript. Now we are going to discuss the whole code in sections.
  • First of all we are going to discuss about basic html code of the given program. Let's see the given lines of code and read the comments in blue to learn the use of these statements:
  • <!DOCTYPE html>
    <html>
    <head>
    <script>
    // Here we will write our whole Javascript code. Please be patient and go further.
    </script>
    </head>
    <body onload="init();">// Here onload element will call our init() function when fired.
    <canvas id="canvas"></canvas>// HTML5 canvas element with id canvas is used to create graphics in html.
    </body>
    </html>
  • Now let's start the Javascript code. So now it's time to be ready for the really very important part of this game. So Let's write Javascript variables and constants once again see the given lines of code and read the comments in blue to learn the use of these JavaScript variables and constants:
  • const DIFFICULTY = 3;// The constant DIFFICULTY contains the number of pieces in our puzzle. Here its value is 3 so we get 9 pieces of puzzles. It shows the medium level of game. As we increase the number of this constant as the difficulty level get increases.
    const HOVER_TINT = '#009900';
    //The next two variables will hold a canvas drawing context and a reference to the canvas,respectively.
    var _stage;
    var _canvas;
    var _loaded_image;//This variable will be a reference to the loaded image.
    var _pieces;
    //The next four variables will be used to store the dimensions of both the entire puzzle and each individual puzzle piece.
    var _puzzleWidth;
    var _puzzleHeight;
    var _pieceWidth;
    var _pieceHeight;
    var _currentPiece;//This variable holds a reference to the piece currently being dragged.
    var _currentDropPiece;//This variable holds a reference to the piece currently in position to be dropped on.
    var _mouse;// This variable is a reference that will hold the mouse's current x and y position.
  • Now you are done with the basic part of Javascript. It's time to move on our Javascript functions. So let's start with init() function. So again see the given lines of code and read the comments in blue to learn the use of JavaScript init() function:
  • function init(){ // This function will load the image for the puzzle.
    _loaded_image = new Image();// This statement shows that the Image object is first instantiated and set to our _loaded_image variable.
    _loaded_image.addEventListener('load',onImage,false);// This statement listen for the load event which will then fire our onImage() function when the image has finished loading.
    _loaded_image.src = "Dora_Hexa_Puzzle.jpg";// Lastly we set the source of the image, which we have to load for the puzzle.
    }
  • Now we have done with the init() function it means the image is successfully loaded but don't get tired its time to learn the next function that is onImage() function. So take some tea or coffee and come to the main point. So again see the given lines of code and read the comments in blue to learn the use of JavaScript onImage() function:
  • function onImage(e){//This function will set the values of variables declared earlier.
    //The next two statements calculate the size of each puzzle piece.
    _pieceWidth = Math.floor(_loaded_image.width / DIFFICULTY)
    _pieceHeight = Math.floor(_loaded_image.height / DIFFICULTY)
    //We use our new puzzle piece values to determine the total size of the puzzle and set these values to variables_puzzleWidth and _puzzleHeight as given below.
    _puzzleWidth = _pieceWidth * DIFFICULTY;
    _puzzleHeight = _pieceHeight * DIFFICULTY;
    //Here we will call the these two function.We will discuss both of these functions further.
    setCanvas();
    initPuzzle();
    }
  • Now it's time to move with the setCanavs() function. So take a little break and then come to the next section that is setCanavs() function. So again see the given lines of code and read the comments in blue to learn the use of setCanavs() function:
  • function setCanvas(){//This function will set the canvas element.
    //Next two statements will set _canvas variable to reference canvas element, and to reference its context respectively.
    _canvas = document.getElementById('canvas');
    _stage = _canvas.getContext('2d');
    //Next two statements set the width and height of canvas to match the size of trimmed image.
    _canvas.width = _puzzleWidth;
    _canvas.height = _puzzleHeight;
    _canvas.style.border = "1px solid cyan";//This statement creates a cyan color border around our canvas to display the bounds of our puzzle.
    }
  • We have done with the setting of canvas element. It's time to initialize the puzzle. To initialize the puzzle we have to use the initPuzzle() function.So again see the given lines of code and read the comments in blue to learn the use of this initPuzzle() function:
  • function initPuzzle(){//This function initialize the puzzle and we can call it again later when we want to replay the puzzle.
    _pieces = [];// This is an empty array.
    _mouse = {x:0,y:0};// This _mouse object will hold our mouse position throughout the application.
    // Now we are going to set the variables _currentPiece and _currentPieceDrop to null .Although on the first play these values would already be null, but we want to make sure they get reset when replaying the puzzle.
    _currentPiece = null;
    _currentDropPiece = null;
    _stage.drawImage(_loaded_image, 0, 0, _puzzleWidth, _puzzleHeight, 0, 0, _puzzleWidth, _puzzleHeight);// This statement will draw the entire image to display to the player what they will be creating.
    // Next two statements will call some functions which we will learn further.
    createTitle("Click the Image to Start Puzzle");
    buildPieces();
    }
  • Now it's time to instructs the user to click the puzzle to begin. To do this we have to use the createTitle() function.So again see the given lines of code and read the comments in blue to learn the use of this createTitle() function:
  • function createTitle(msg){// This function instructs the user to click the puzzle to begin.
    // Our title will be a semi-transparent rectangle that will serve as the background of our text. This allows the user to see the image behind it and also assures our white text will be legible on any image.We simply set fillStyle to black and globalAlpha to .4, before filling in a short black rectangle at the bottom of the image.
    _stage.fillStyle = "#000000";
    _stage.globalAlpha = .4;
    _stage.fillRect(100,_puzzleHeight - 40,_puzzleWidth - 200,40);
    _stage.fillStyle = "#FFFFFF";
    _stage.globalAlpha = 1;// This statement will set the golbalAlpha to 1(opaque) before drawing the text because globalAlpha affects the entire canvas.
    // The next three statements set the text alignment and font properties for the title.
    _stage.textAlign = "center";
    _stage.textBaseline = "middle";
    _stage.font = "20px Arial";
    // The fillText() method will draw the text.We have passed msg variable in the method and place it at the horizontal center of the canvas, and the vertical center of the rectangle.
    _stage.fillText(msg,_puzzleWidth / 2,_puzzleHeight - 20);
    }
  • Now it's time to build the puzzle pieces.To do this we have to use the buildPieces() function.So again see the given lines of code and read the comments in blue to learn the use of this buildPieces() function:
  • function buildPieces(){// This function build the puzzle pieces.
    // Next four statement shows the declaration of four Javascript variable which we will use in the further loop.
    var i;
    var piece;
    var xPos = 0;
    var yPos = 0;
    // Discussion of loop is given in next point.
    for(i = 0;i < DIFFICULTY * DIFFICULTY;i++){
    piece = {};
    piece.sx = xPos;
    piece.sy = yPos;
    _pieces.push(piece);
    xPos += _pieceWidth;
    if(xPos >= _puzzleWidth){
    xPos = 0;
    yPos += _pieceHeight;
    }
    }
    document.onmousedown = shufflePuzzle;
    }
  • First of all we create an empty array object piece. After this we will add the properties sx and sy to the object. In the first iteration, these values are 0 and represent that point in our image from where we have to begin the drawing. Now we have to push it to the _pieces[] array. This object will also contain the properties xPos and yPos, which will tell us the current position in the puzzle where the piece should be drawn.
  • We'll be shuffling the objects before its playable so these values don't need to be set quite yet.The last thing we do in each loop is increase the local variable xPos by _pieceWidth. Before continuing on with the loop, we determine if we need to step down to the next row of pieces by checking whether xPos is beyond the width of the puzzle. If so, we reset xPos back to 0 and increase yPos by _pieceHeight.
  • Now we have our puzzle pieces all stored away nicely in our _pieces array. At this point the code finally stops executing and waits for the user to interact. We set a click listener to the document to fire the shufflePuzzle() function when triggered, which will begin the game.
  • It was very tiring. So relax for some time and take a deep breath and start with next function that is shufflePuzzle().
  • function shufflePuzzle(){
    _pieces = shuffleArray(_pieces);
    _stage.clearRect(0,0,_puzzleWidth,_puzzleHeight);
    var i;
    var piece;
    var xPos = 0;
    var yPos = 0;
    for(i = 0;i < _pieces.length;i++){
    piece = _pieces[i];

    piece.xPos = xPos;
    piece.yPos = yPos;
    _stage.drawImage(_loaded_image, piece.sx, piece.sy, _pieceWidth, _pieceHeight, xPos, yPos, _pieceWidth, _pieceHeight);
    _stage.strokeRect(xPos, yPos, _pieceWidth,_pieceHeight);
    xPos += _pieceWidth;
    if(xPos >= _puzzleWidth){
    xPos = 0;
    yPos += _pieceHeight;
    }
    }
    document.onmousedown = onPuzzleClick;
    }
  • This function will shuffled our puzzle-pieces. The explanation of this function can't be given here.So assume that your puzzle pieces are successfully suffeled. Now let's begin with the another function that is onPuzzleClick(). This function will determine what piece was clicked on.
  • function onPuzzleClick(e){// This function will determine what piece was clicked on.
    if(e.layerX || e.layerX == 0){
    _mouse.x = e.layerX - _canvas.offsetLeft;
    _mouse.y = e.layerY - _canvas.offsetTop;
    }
    else if(e.offsetX || e.offsetX == 0){
    _mouse.x = e.offsetX - _canvas.offsetLeft;
    _mouse.y = e.offsetY - _canvas.offsetTop;
    }
    _currentPiece = checkPieceClicked();
    if(_currentPiece != null){
    _stage.clearRect(_currentPiece.xPos,_currentPiece.yPos,_pieceWidth,_pieceHeight);
    _stage.save();
    _stage.globalAlpha = .9;
    _stage.drawImage(_loaded_image, _currentPiece.sx, _currentPiece.sy, _pieceWidth, _pieceHeight, _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2),
    _pieceWidth, _pieceHeight);
    _stage.restore();
    document.onmousemove = updatePuzzle;
    document.onmouseup = pieceDropped;
    }
    }
  • The explanation of this function is also beyond the topic of this tutorial so we'll move on to the next function that is checkPieceClicked().

    function checkPieceClicked(){
    var i;
    var piece;
    for(i = 0;i < _pieces.length;i++){
    piece = _pieces[i];
    if(_mouse.x < piece.xPos || _mouse.x > (piece.xPos + _pieceWidth) || _mouse.y < piece.yPos || _mouse.y > (piece.yPos + _pieceHeight)){
    //PIECE NOT HIT
    }
    else{
    return piece;
    }
    }
    return null;
    }
  • We were able to determine what piece was clicked, but how did we do it? We can determine this with the help of checkPieceClicked() function. It is very easy to do.What we need to do is loop through all of the puzzle pieces and determine if the click was within the bounds of any of our objects. If we find one, we return the matched object and end the function. If we find nothing, we return null.
  • Now back to the dragging. We call updatePuzzle() function when the user moves the mouse. This is the biggest function of the application as it's doing several things.
  • function updatePuzzle(e){
    _currentDropPiece = null;
    if(e.layerX || e.layerX == 0){
    _mouse.x = e.layerX - _canvas.offsetLeft;
    _mouse.y = e.layerY - _canvas.offsetTop;
    }
    else if(e.offsetX || e.offsetX == 0){
    _mouse.x = e.offsetX - _canvas.offsetLeft;
    _mouse.y = e.offsetY - _canvas.offsetTop;
    }
    _stage.clearRect(0,0,_puzzleWidth,_puzzleHeight);
    var i;
    var piece;
    for(i = 0;i < _pieces.length;i++){
    piece = _pieces[i];
    if(piece == _currentPiece){
    continue;
    }
    _stage.drawImage(_loaded_image, piece.sx, piece.sy, _pieceWidth, _pieceHeight, piece.xPos, piece.yPos, _pieceWidth, _pieceHeight);
    _stage.strokeRect(piece.xPos, piece.yPos, _pieceWidth,_pieceHeight);
    if(_currentDropPiece == null){
    if(_mouse.x < piece.xPos || _mouse.x > (piece.xPos + _pieceWidth) || _mouse.y < piece.yPos || _mouse.y > (piece.yPos + _pieceHeight)){
    //NOT OVER
    }
    else{
    _currentDropPiece = piece;
    _stage.save();
    _stage.globalAlpha = .4;
    _stage.fillStyle = HOVER_TINT;
    _stage.fillRect(_currentDropPiece.xPos,_currentDropPiece.yPos,_pieceWidth, _pieceHeight);
    _stage.restore();
    }
    }
    }
    _stage.save();
    _stage.globalAlpha = .6;
    _stage.drawImage(_loaded_image, _currentPiece.sx, _currentPiece.sy, _pieceWidth, _pieceHeight, _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth, _pieceHeight);
    _stage.restore();
    _stage.strokeRect( _mouse.x - (_pieceWidth / 2), _mouse.y - (_pieceHeight / 2), _pieceWidth,_pieceHeight);
    }
  • Now we are now successfully dragging a puzzle piece and even getting visual feedback on where it will be dropped. Now all that is left is to drop the piece. This will be done by pieceDropped() function.
  • function pieceDropped(e){
    document.onmousemove = null;
    document.onmouseup = null;
    if(_currentDropPiece != null){
    var tmp = {xPos:_currentPiece.xPos,yPos:_currentPiece.yPos};
    _currentPiece.xPos = _currentDropPiece.xPos;
    _currentPiece.yPos = _currentDropPiece.yPos;
    _currentDropPiece.xPos = tmp.xPos;
    _currentDropPiece.yPos = tmp.yPos;
    } resetPuzzleAndCheckWin();
    }
  • Once again, clear the canvas and set up a gameWin variable, setting it to true by default. Now proceed with our all-too-familiar pieces loop.

    The code here should look familiar so we won't go over it. It simply draws the pieces back into their original or new slots. Within this loop, we want to see if each piece is being drawn in its winning position. This is simple: we check to see if our sx and sy properties match up with xPos and yPos. If not, we know we couldn't possibly win the puzzle and set gameWin to false. If we made it through the loop with everyone in their winning places, we set up a quick timeout to call our gameOver() method. (We set a timeout so the screen doesn't change so drastically upon dropping the puzzle piece.)
  • function resetPuzzleAndCheckWin(){
    _stage.clearRect(0,0,_puzzleWidth,_puzzleHeight);
    var gameWin = true;
    var i;
    var piece;
    for(i = 0;i < _pieces.length;i++){
    piece = _pieces[i];
    _stage.drawImage(_loaded_image, piece.sx, piece.sy, _pieceWidth, _pieceHeight, piece.xPos, piece.yPos, _pieceWidth, _pieceHeight);
    _stage.strokeRect(piece.xPos, piece.yPos, _pieceWidth,_pieceHeight);
    if(piece.xPos != piece.sx || piece.yPos != piece.sy){
    gameWin = false;
    }
    }
    if(gameWin){
    setTimeout(gameOver,500);
    }
    }
  • Ahh, It was too tiring. So let's begin with our last function that is gameOver() function:
  • function gameOver(){
    document.onmousedown = null;
    document.onmousemove = null;
    document.onmouseup = null;
    initPuzzle();
    }
  • Here we just remove all listeners and call initPuzzle(), which resets all necessary values and waits for the user to play again.
  • That's it..