A Step-by-Step Tutorial for Beginners
🎯 Introduction
In this tutorial, we'll build a complete 2D game from scratch using HTML5 Canvas, CSS, and vanilla JavaScript. You'll learn:
✅ Canvas rendering fundamentals
✅ Game loop architecture
✅ Keyboard input handling
✅ Collision detection
✅ Score tracking
By the end, you'll have a polished game featuring:
-
A player-controlled character
-
A moving enemy
-
Collectible coins
-
Score tracking
🛠️ Part 1: Project Setup (3 Files)
1.1 HTML Structure
Create index.html
with this foundation:
<!DOCTYPE html>
<html>
<head>
<title>Coin Collector</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas"></canvas>
</div>
<script src="game.js"></script>
</body>
</html>
Key Features:
-
Clean HTML structure
-
Logical file separation
1.2 CSS Styling
Create style.css
with these settings:
/* Centers game and adds visual polish */
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: #f0f0f0;
font-family: Arial, sans-serif;
}
#gameContainer {
border: 4px solid #333;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
}
canvas {
display: block;
background: #222;
}
Key Features:
-
Centers the game perfectly in the viewport
-
Adds a border and shadow
-
Sets up a dark game background
1.3 Canvas Initialization
Create game.js
with these initial settings:
// Canvas setup
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
// Game dimensions
canvas.width = 600;
canvas.height = 400;
Why These Dimensions?
600x400 provides a good balance:
-
Large enough for clear gameplay
-
Small enough to work on mobile devices
-
Maintains aspect ratio when scaled
👾 Part 2: Creating Game Objects
2.1 Player Character
const player = {
x: 50, y: 50, // Starting position
size: 30, // Width/height
color: "#4CAF50", // Green color
speed: 4 // Movement speed
};
2.2 Enemy Character
const enemy = {
x: 300, y: 200, // Starting position
size: 40, // Slightly larger than player
color: "#FF5252", // Red color
speed: 3, // Movement speed
dx: 1.5, dy: 0, // Movement direction
lastDirectionChange: 0 // Timer for direction changes
};
2.3 Collectible Coin
const coin = {
x: 0, y: 0, // Position set when spawned
size: 15, // Smaller than player
color: "#FFD700", // Gold color
collected: false // Track collection state
};
let score = 0; // Player score counter
🎮 Part 3: Implementing Game Logic
3.1 Drawing Functions
function drawPlayer() {
ctx.fillStyle = player.color;
ctx.fillRect(player.x, player.y, player.size, player.size);
}
function drawEnemy() {
ctx.fillStyle = enemy.color;
ctx.fillRect(enemy.x, enemy.y, enemy.size, enemy.size);
}
function drawCoin() {
if (coin.collected) return;
ctx.beginPath();
ctx.arc(coin.x + coin.size/2, coin.y + coin.size/2,
coin.size/2, 0, Math.PI * 2);
ctx.fillStyle = coin.color;
ctx.fill();
}
Key Details:
-
fillRect()
for square player/enemy -
arc()
for circular coins -
Conditional rendering for collected coins
3.2 Movement Systems
// Keyboard controls
const keys = {
ArrowUp: false,
ArrowDown: false,
ArrowLeft: false,
ArrowRight: false
};
document.addEventListener("keydown", (e) => {
if (e.key in keys) keys[e.key] = true;
});
document.addEventListener("keyup", (e) => {
if (e.key in keys) keys[e.key] = false;
});
// Player movement
function updatePlayer() {
if (keys.ArrowUp) player.y -= player.speed;
if (keys.ArrowDown) player.y += player.speed;
if (keys.ArrowLeft) player.x -= player.speed;
if (keys.ArrowRight) player.x += player.speed;
// Screen boundaries
player.x = Math.max(0, Math.min(canvas.width - player.size, player.x));
player.y = Math.max(0, Math.min(canvas.height - player.size, player.y));
}
// Enemy AI
function updateEnemy() {
// Change direction every 1 second
if (Date.now() - enemy.lastDirectionChange > 1000) {
enemy.dx = Math.random() > 0.5 ? enemy.speed : -enemy.speed;
enemy.dy = Math.random() > 0.5 ? enemy.speed : -enemy.speed;
enemy.lastDirectionChange = Date.now();
}
enemy.x += enemy.dx;
enemy.y += enemy.dy;
// Bounce off walls
if (enemy.x <= 0 || enemy.x >= canvas.width - enemy.size) enemy.dx *= -1;
if (enemy.y <= 0 || enemy.y >= canvas.height - enemy.size) enemy.dy *= -1;
}
⚙️ Part 4: Game Systems
4.1 Collision Detection
function checkCollisions() {
// Enemy collision
if (isColliding(player, enemy)) {
alert(`Game Over! Score: ${score}`);
resetGame();
}
// Coin collection
if (!coin.collected && isColliding(player, coin)) {
score += 10;
spawnCoin();
}
}
function isColliding(obj1, obj2) {
return (
obj1.x < obj2.x + obj2.size &&
obj1.x + obj1.size > obj2.x &&
obj1.y < obj2.y + obj2.size &&
obj1.y + obj1.size > obj2.y
);
}
4.2 Game Loop
function gameLoop() {
// Clear frame
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update game state
updatePlayer();
updateEnemy();
checkCollisions();
// Render objects
drawPlayer();
drawEnemy();
drawCoin();
// Draw UI
ctx.fillStyle = "white";
ctx.font = "20px Arial";
ctx.fillText(`Score: ${score}`, 20, 30);
// Continue loop
requestAnimationFrame(gameLoop);
}
// Initialize game
function spawnCoin() {
coin.x = 10 + Math.random() * (canvas.width - coin.size - 20);
coin.y = 10 + Math.random() * (canvas.height - coin.size - 20);
coin.collected = false;
}
function resetGame() {
// Reset player
player.x = 50;
player.y = 50;
// Reset enemy
enemy.x = 300;
enemy.y = 200;
enemy.dx = 1.5;
enemy.dy = 0;
enemy.lastDirectionChange = 0;
// Reset game state
score = 0;
Object.keys(keys).forEach(key => keys[key] = false);
spawnCoin();
}
// Start game
spawnCoin();
gameLoop();
🚀 Conclusion & Next Steps
You've now built a complete game with:
-
Player controls
-
Enemy AI
-
Collectible items
-
Score tracking
Enhancement Ideas:
-
Add sound effects
-
Implement multiple levels
-
Create power-ups
-
Add mobile touch controls
Happy coding! Try experimenting with the values to create your own game variations.