Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Building Progressive Web Games with JavaScript and HTML5 Canvas
In recent years, the web platform has evolved significantly, enabling developers to create more powerful and interactive applications. With the introduction of HTML5 and JavaScript, developers now have the tools to build not just websites but also games that can run directly in the browser.
In this article, we will explore the process of building a Progressive Web Game using JavaScript and HTML5 Canvas, with a practical example of a "Brick Breaker" game.
What are Progressive Web Games?
Progressive Web Games are web-based games that leverage modern web technologies to provide a rich and immersive gaming experience. They are built using standard web technologies like HTML, CSS, and JavaScript, making them accessible across different platforms and devices. One of the key features of Progressive Web Games is their ability to work offline and provide an app-like experience to users.
Setting up the Game
To get started, we need a canvas element where we can render our game graphics. The canvas element provides a drawing surface on which we can create dynamic and interactive graphics using JavaScript. Here's the HTML markup for our game canvas:
<canvas id="gameCanvas" width="480" height="320"></canvas>
In the above code, we define a canvas element with an id of "gameCanvas" and specify the width and height of the canvas. Feel free to adjust the dimensions based on your preference.
Drawing the Game Elements
Now that we have our canvas set up, let's move on to drawing the game elements. In our "Brick Breaker" game example, we have three main elements: the ball, the paddle, and the bricks. We will use JavaScript and the HTML5 Canvas API to draw these elements on the canvas.
The Ball
The ball will be represented as a circle on the canvas. We define its position (x, y), radius, and movement (dx, dy). We use the context.arc() method to draw the ball on the canvas. Here's the code for drawing the ball:
function drawBall() {
context.beginPath();
context.arc(x, y, ballRadius, 0, Math.PI * 2);
context.fillStyle = "#0095DD";
context.fill();
context.closePath();
}
The Paddle
The paddle is a rectangular shape that moves horizontally. We define its position (paddleX), width, and height. We use the context.rect() method to draw the paddle on the canvas. Here's the code for drawing the paddle:
function drawPaddle() {
context.beginPath();
context.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
context.fillStyle = "#0095DD";
context.fill();
context.closePath();
}
The Bricks
The bricks are represented as rectangles arranged in rows and columns. We define their positions, width, height, and status (whether they are active or destroyed). We use nested loops to iterate through the bricks array and draw the active bricks on the canvas. Here's the code for drawing the bricks:
function drawBricks() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
if (bricks[c][r].status === 1) {
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
context.beginPath();
context.rect(brickX, brickY, brickWidth, brickHeight);
context.fillStyle = "#0095DD";
context.fill();
context.closePath();
}
}
}
}
Complete Game Implementation
Below is the complete code for the Brick Breaker game. The game includes collision detection, scoring, and keyboard controls for moving the paddle:
Example
index.html
<!DOCTYPE html>
<html>
<head>
<title>Brick Breaker Game</title>
<style>
canvas {
border: 1px solid #000;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="480" height="320"></canvas>
<script>
const canvas = document.getElementById("gameCanvas");
const context = canvas.getContext("2d");
const ballRadius = 10;
let x = canvas.width / 2;
let y = canvas.height - 30;
let dx = 2;
let dy = -2;
const paddleHeight = 10;
const paddleWidth = 75;
let paddleX = (canvas.width - paddleWidth) / 2;
let rightPressed = false;
let leftPressed = false;
const brickRowCount = 3;
const brickColumnCount = 5;
const brickWidth = 75;
const brickHeight = 20;
const brickPadding = 10;
const brickOffsetTop = 30;
const brickOffsetLeft = 30;
const bricks = [];
for (let c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (let r = 0; r < brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1 };
}
}
let score = 0;
function drawBall() {
context.beginPath();
context.arc(x, y, ballRadius, 0, Math.PI * 2);
context.fillStyle = "#0095DD";
context.fill();
context.closePath();
}
function drawPaddle() {
context.beginPath();
context.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
context.fillStyle = "#0095DD";
context.fill();
context.closePath();
}
function drawBricks() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
if (bricks[c][r].status === 1) {
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
context.beginPath();
context.rect(brickX, brickY, brickWidth, brickHeight);
context.fillStyle = "#0095DD";
context.fill();
context.closePath();
}
}
}
}
function drawScore() {
context.font = "16px Arial";
context.fillStyle = "#0095DD";
context.fillText("Score: " + score, 8, 20);
}
function collisionDetection() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
const brick = bricks[c][r];
if (brick.status === 1) {
if (
x > brick.x &&
x < brick.x + brickWidth &&
y > brick.y &&
y < brick.y + brickHeight
) {
dy = -dy;
brick.status = 0;
score++;
if (score === brickRowCount * brickColumnCount) {
alert("Congratulations! You win!");
document.location.reload();
}
}
}
}
}
}
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
drawBricks();
drawBall();
drawPaddle();
drawScore();
collisionDetection();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy < ballRadius) {
dy = -dy;
} else if (y + dy > canvas.height - ballRadius) {
if (x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
} else {
alert("Game Over");
document.location.reload();
}
}
if (rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 7;
} else if (leftPressed && paddleX > 0) {
paddleX -= 7;
}
x += dx;
y += dy;
requestAnimationFrame(draw);
}
function keyDownHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight") {
rightPressed = true;
} else if (e.key === "Left" || e.key === "ArrowLeft") {
leftPressed = true;
}
}
function keyUpHandler(e) {
if (e.key === "Right" || e.key === "ArrowRight") {
rightPressed = false;
} else if (e.key === "Left" || e.key === "ArrowLeft") {
leftPressed = false;
}
}
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
draw();
</script>
</body>
</html>
Game Controls
The game uses keyboard controls for player interaction:
- Left Arrow Key - Move paddle left
- Right Arrow Key - Move paddle right
The game automatically handles ball physics, collision detection with walls, paddle, and bricks. Players win by destroying all bricks and lose if the ball falls below the paddle.
Key Features
This Progressive Web Game demonstrates several important concepts:
- Canvas Rendering - Using HTML5 Canvas API for drawing game elements
- Animation Loop - Using requestAnimationFrame for smooth gameplay
- Collision Detection - Implementing physics for ball-brick and ball-paddle interactions
- User Input - Handling keyboard events for game controls
- Game State Management - Tracking score and game over conditions
Conclusion
Building Progressive Web Games with JavaScript and HTML5 Canvas opens up a world of possibilities for cross-platform game development. This Brick Breaker example demonstrates core game development concepts including rendering, physics, and user interaction that can be extended to create more complex games.
