Create the classic game pong using JavaScript and HTML5 canvas

To kick us off into the weekend here’s all you’ll need to get started on creating a simplified version of an old classic game pong using JavaScript and HTML5 Canvas.

Who over 40 doesn’t remember playing this old nugget? Pong.

This post isn’t intended to be a tutorial, it’s just a little bit of fun to see us into the weekend and help me launch my new blog.

The majority of content I’ll be posting on the blog will focus on user experience research and design, google analytics and marketing, some agile content, and a little bit of front-end and responsive development.

The first thing we’ll do is create our HTML5 template (for those using Sublime Text with Emmet installed, simply type the ! and press enter), and place a canvas element between the body tags. I’ve also used a web font as the game just doesn’t look retro enough using Arial.

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Classic Pong</title>

	<!-- Use a retro game style font. -->
	<style>
		@import url(https://fonts.googleapis.com/css?family=VT323);
	</style>
</head>
<body>
<!-- Canvas often encounters issues with font load times. To speed this up we declared the font before drawing the canvas. -->
<span style="font-family: 'VT323', monospace;"> </span>
<!-- Here we draw the canvas -->
<canvas id="gameCanvas" width="800" height="600"></canvas>
</body>
</html>

Declare the variables and the constants that we’ll use in the game, all the elements below have comments to explain the usage.

var canvas;
var canvasContext;
var ballX = 50;
var ballY = 50;
var ballSpeedX = 10;
var ballSpeedY = 4;

// Right and left paddles.
var paddle1Y = 250;
var paddle2Y = 250;
const PADDLE_THICKNESS = 10;
const PADDLE_HEIGHT = 100;

// player1Score and player2Score holds the player scores. WINNING_SCORE is set to 3 (first player to reach 3).
var player1Score = 0;
var player2Score = 0;
const WINNING_SCORE = 3;

// showWinScreen can be set to true or false. Though we don't want it showing on load.
var showingWinScreen = false;

// Create our sound objects for the paddles and upper/lower walls.
soundEfx1 = new Audio('pong4.wav');
soundEfx2 = new Audio('pong5.wav');
soundEfx3 = new Audio('pong3.wav');

We’ve created a couple of mouse functions to control the game. Firstly, we create a function to look for the mouse position and control the left paddle, then we use a mouse click to restart the game.

// We need to get the mouse position in order to move the left paddle.
function calculateMousePos(evt) {
	var rect = canvas.getBoundingClientRect();
	var root = document.documentElement;
	var mouseX = evt.clientX - rect.left - root.scrollLeft;
	var mouseY = evt.clientY - rect.top - root.scrollTop;
	return {
		x:mouseX,
		y:mouseY
	};
}

// Mouse click to restart the game.
function handleMouseClick(evt){
	if (showingWinScreen) {
		player1Score = 0;
		player2Score = 0;
		showingWinScreen = false;
	}
}

We’re going to call the next functions on page load, draw the canvas object and use the mouse functions.

// Create the canvas and state the font size and family.
window.onload = function() {
	canvas = document.getElementById('gameCanvas');
	canvasContext = canvas.getContext('2d');
	canvasContext.font = '60px VT323';
// The speed of the ball
	var framesPerSecond = 30;
	setInterval(function() {
			moveEverything();
			drawEverything();	
		}, 1000/framesPerSecond);
// Mouse click to restart the game, and movement to control the left paddle
	canvas.addEventListener("mousedown", handleMouseClick);
	canvas.addEventListener('mousemove',
		function(evt) {
			var mousePos = calculateMousePos(evt);
			paddle1Y = mousePos.y - (PADDLE_HEIGHT/2);
		});
}
function colorRect(leftX,topY, width,height, drawColor) {
	canvasContext.fillStyle = drawColor;
	canvasContext.fillRect(leftX,topY, width,height);
}

We need to show a win screen when the score is reached.

// Show the winning screen when score is reached and start the ball from the center
function ballReset() {
	if (player1Score >= WINNING_SCORE ||
		player2Score >= WINNING_SCORE) {
		showingWinScreen = true;
	}
	ballSpeedX = -ballSpeedX;
	ballX = canvas.width/2;
	ballY = canvas.height/2;
}

Let’s give the computer control of the right paddle, but don’t make it too easy to win.

// Compute the movement of the right paddle - don't want to make it too hard to win against the computer
function computerMovement() {
	var paddle2YCenter = paddle2Y + (PADDLE_HEIGHT/2)
	if(paddle2YCenter < ballY - 35) {
		paddle2Y += 6;
	} else if(paddle2YCenter > ballY + 35) {
		paddle2Y -= 6;
	}
}

Let’s make it even harder to beat the computer by changing velocity and direction of the ball off the paddles.

// Here we'll give the ball some direction and velocity depending on where it hits the paddle, plus add some sound effects
function moveEverything() {
	if (showingWinScreen) {
		return;
	}
	computerMovement();

	ballX += ballSpeedX;
	ballY += ballSpeedY;
	
	if(ballX < 0) {
		if(ballY > paddle1Y &&
			ballY < paddle1Y+PADDLE_HEIGHT) {
			ballSpeedX = -ballSpeedX;
			soundEfx2.play();
			var deltaY = ballY
				-(paddle1Y+PADDLE_HEIGHT/2);
				ballSpeedY = deltaY * 0.35;
		} else {
			player2Score++; //Must be before ball reset
			ballReset();
				
		}
	}
	if(ballX > canvas.width) {
		if(ballY > paddle2Y &&
			ballY < paddle2Y+PADDLE_HEIGHT) {
			ballSpeedX = -ballSpeedX;
		    soundEfx1.play();
		var deltaY = ballY
				-(paddle2Y+PADDLE_HEIGHT/2);
				ballSpeedY = deltaY * 0.35;
		} else {
			player1Score++; //Must be before ball reset
			ballReset();
			
		}
	}
	if(ballY < 0) {
		ballSpeedY = -ballSpeedY;
		soundEfx3.play();
	}
	if(ballY > canvas.height) {
		ballSpeedY = -ballSpeedY;
		soundEfx3.play();
	}
}

Lastly, we’ll draw all the items inside the canvas.


// Let's draw all the items on the canvas
function drawNet() {
	for (var i = 0; i <canvas.height; i+=40) {
		colorRect(canvas.width/2-2,i,4,20,'white');
	}
}
function drawEverything() {
	// next line blanks out the screen with black
	colorRect(0,0,canvas.width,canvas.height,'black');

	if (showingWinScreen) {
		canvasContext.fillStyle = "#fff";
		if (player1Score >= WINNING_SCORE) {
			canvasContext.fillText("Winner winner chicken dinner!", 50,200);
		}	else if (player2Score >= WINNING_SCORE) {
			canvasContext.fillText("Only winners are grinners!", 100,200);
		}
		canvasContext.fillText("Click to play again", 150,500);
		return;
	}

	drawNet();
	// this is left player paddle
	colorRect(0,paddle1Y,PADDLE_THICKNESS,PADDLE_HEIGHT,'white');
	// this is right computer paddle
	colorRect(canvas.width-PADDLE_THICKNESS,paddle2Y,PADDLE_THICKNESS,PADDLE_HEIGHT,'white');
	// This next line draws a round ball. To use un-comment the colorCircle function below
	//colorCircle(ballX, ballY, 10, 'white');
	// This creates a rectangle ball. This is the traditional shape for the ball.
	colorRect(ballX, ballY, 15, 15, 'white');
	canvasContext.fillText(player1Score, 300,100);
	canvasContext.fillText(player2Score, canvas.width-300,100);
}

// If you want to use a round ball then uncomment the colorCircle item above, and the function below.
// function colorCircle(centerX, centerY, radius, drawColor) {
// 	canvasContext.fillStyle = drawColor;
// 	canvasContext.beginPath();
// 	canvasContext.arc(centerX, centerY, radius, 0,Math.PI*2,true);
// 	canvasContext.fill();
// }

// function colorRect(leftX,topY, width,height, drawColor) {
// 	canvasContext.fillStyle = drawColor;
// 	canvasContext.fillRect(leftX,topY, width,height);
// }

Hope your game look like the one below. Have fun, but don’t wag too much work doing so 🙂

By |2017-08-11T04:43:22+00:00July 30th, 2017|Development, JavaScript|0 Comments

About the Author:

GradDip Online Communications - with over 18 years experience in the online and digital space doing business analysis and requirements gathering, user experience research and design, UI design and development, project management, and front-end web development with a strong focus on responsive mobile first design.

Leave A Comment