Introduction
Before the era of photorealistic 3D worlds, there was Pong. This simple table tennis-style arcade game, released in 1972, is a cornerstone of video game history. More importantly for us developers, it's the perfect project to understand the core concepts of game development.
In this tutorial, we'll build a fully functional Pong game using just the holy trinity of web development: HTML, CSS, and JavaScript. You'll learn about:
- The Game Loop: The heartbeat of any game.
- Rendering with Canvas: Drawing and animating game objects.
- Collision Detection: Making the ball interact with paddles and walls.
- User Input Handling: Controlling the paddles with keyboard events.
Let's get started!
Project Structure
We'll keep it simple with three files in a single directory:
pong-game/
├── index.html
├── style.css
└── script.jsStep 1: The HTML Foundation (index.html)
The HTML file is straightforward. We set up a <!DOCTYPE html>, link our CSS and JavaScript files, and most importantly, create a <canvas> element. This canvas is where all the game's visuals will be drawn.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Classic Pong Game</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>PONG</h1>
<canvas id="gameCanvas" width="800" height="400"></canvas>
<div class="score">
<span>Player: <span id="playerScore">0</span></span>
<span>Computer: <span id="computerScore">0</span></span>
</div>
<button id="startButton">Start Game</button>
</div>
<script src="script.js"></script>
</body>
</html>Step 2: Basic Styling with CSS (style.css)
We'll use CSS to center the game, style the canvas with a classic arcade feel, and make the score display prominent.
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(to bottom, #1a1a2e, #16213e);
font-family: 'Courier New', monospace;
color: #fff;
}
.container {
text-align: center;
}
h1 {
font-size: 3em;
margin-bottom: 20px;
color: #0fcc45; /* Classic Arcade Green */
text-shadow: 0 0 10px #0fcc45;
}
#gameCanvas {
border: 4px solid #0fcc45;
border-radius: 5px;
box-shadow: 0 0 20px rgba(15, 204, 69, 0.5);
background-color: #000;
}
.score {
font-size: 1.5em;
margin: 20px 0;
display: flex;
justify-content: space-around;
}
#startButton {
padding: 12px 30px;
font-size: 1.2em;
font-family: 'Courier New', monospace;
background-color: #0fcc45;
color: #000;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
transition: all 0.2s ease;
}
#startButton:hover {
background-color: #0daa3a;
box-shadow: 0 0 15px #0fcc45;
}Step 3: The Game Logic with JavaScript (script.js)
This is the heart of our Pong game. We'll break it down into manageable parts.
Part 1: Initial Setup and Variables
First, we get the canvas and its drawing context. Then, we define all the variables for our game objects and state.
// Canvas Setup
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startButton = document.getElementById('startButton');
// Game Objects & Properties
const paddleHeight = 100;
const paddleWidth = 10;
const ballSize = 10;
let playerY = (canvas.height - paddleHeight) / 2;
let computerY = (canvas.height - paddleHeight) / 2;
let ballX = canvas.width / 2;
let ballY = canvas.height / 2;
let ballSpeedX = 5;
let ballSpeedY = 5;
// Scores
let playerScore = 0;
let computerScore = 0;
// Control State
let upPressed = false;
let downPressed = false;
// Game State
let gameRunning = false;Part 2: User Input Handling
We listen for keyboard events to control the player's paddle.
// Event Listeners for Paddle Movement
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') upPressed = true;
if (e.key === 'ArrowDown') downPressed = true;
});
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowUp') upPressed = false;
if (e.key === 'ArrowDown') downPressed = false;
});
// Start Game Button
startButton.addEventListener('click', () => {
if (!gameRunning) {
resetBall();
gameRunning = true;
startButton.textContent = 'Restart Game';
gameLoop();
} else {
// Reset the game on restart
playerScore = 0;
computerScore = 0;
document.getElementById('playerScore').textContent = '0';
document.getElementById('computerScore').textContent = '0';
resetBall();
}
});Part 3: The Core Game Functions
These functions handle the game's logic: moving objects, checking for collisions, and updating the score.
// Move the player paddle based on key presses
function movePlayerPaddle() {
const paddleSpeed = 7;
if (upPressed && playerY > 0) {
playerY -= paddleSpeed;
}
if (downPressed && playerY < canvas.height - paddleHeight) {
playerY += paddleSpeed;
}
}
// Simple AI for the computer paddle
function moveComputerPaddle() {
const computerPaddleCenter = computerY + paddleHeight / 2;
const computerSpeed = 5; // Adjust for difficulty
if (computerPaddleCenter < ballY - 10) {
computerY += computerSpeed;
} else if (computerPaddleCenter > ballY + 10) {
computerY -= computerSpeed;
}
// Keep computer paddle within canvas
computerY = Math.max(0, Math.min(canvas.height - paddleHeight, computerY));
}
// Move the ball and handle wall/paddle collisions
function moveBall() {
ballX += ballSpeedX;
ballY += ballSpeedY;
// Top and Bottom Wall Collision
if (ballY <= 0 || ballY >= canvas.height - ballSize) {
ballSpeedY = -ballSpeedY;
}
// Paddle Collision
// Player Paddle
if (ballX <= paddleWidth &&
ballY > playerY &&
ballY < playerY + paddleHeight) {
ballSpeedX = -ballSpeedX;
// Add a slight angle based on where the ball hits the paddle
let deltaY = ballY - (playerY + paddleHeight / 2);
ballSpeedY = deltaY * 0.2;
}
// Computer Paddle
if (ballX >= canvas.width - paddleWidth - ballSize &&
ballY > computerY &&
ballY < computerY + paddleHeight) {
ballSpeedX = -ballSpeedX;
let deltaY = ballY - (computerY + paddleHeight / 2);
ballSpeedY = deltaY * 0.2;
}
// Score Detection
if (ballX < 0) {
computerScore++;
document.getElementById('computerScore').textContent = computerScore;
resetBall();
}
if (ballX > canvas.width) {
playerScore++;
document.getElementById('playerScore').textContent = playerScore;
resetBall();
}
}
// Reset the ball to the center with a random direction
function resetBall() {
ballX = canvas.width / 2;
ballY = canvas.height / 2;
ballSpeedX = -ballSpeedX; // Alternate who serves
ballSpeedY = (Math.random() * 4 - 2); // Random vertical angle
}Part 4: Drawing on the Canvas
This function is responsible for rendering everything on the screen.
// Draw all game objects onto the canvas
function draw() {
// Clear the canvas
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw the center line (dashed)
ctx.setLineDash([5, 15]);
ctx.beginPath();
ctx.moveTo(canvas.width / 2, 0);
ctx.lineTo(canvas.width / 2, canvas.height);
ctx.strokeStyle = 'white';
ctx.stroke();
ctx.setLineDash([]);
// Draw the paddles
ctx.fillStyle = '#0fcc45';
ctx.fillRect(0, playerY, paddleWidth, paddleHeight); // Player paddle
ctx.fillRect(canvas.width - paddleWidth, computerY, paddleWidth, paddleHeight); // Computer paddle
// Draw the ball
ctx.beginPath();
ctx.arc(ballX, ballY, ballSize, 0, Math.PI * 2);
ctx.fillStyle = 'white';
ctx.fill();
ctx.closePath();
}Part 5: The Game Loop
The game loop is the engine of our game. It runs continuously, updating the game state and re-drawing the screen.
// The main game loop
function gameLoop() {
if (!gameRunning) return;
movePlayerPaddle();
moveComputerPaddle();
moveBall();
draw();
// Use requestAnimationFrame for smooth animation
requestAnimationFrame(gameLoop);
}
// Initial draw to show the game before starting
draw();Conclusion and Next Steps
Congratulations! You've just built a classic Pong game from the ground up. You now have a practical understanding of the game loop, collision detection, and canvas rendering.
- To take this project further, consider these enhancements:
- Difficulty Levels: Increase the
computerSpeedor make the computer's movement prediction smarter. - Sound Effects: Add
Audioobjects for paddle hits, scoring, and wall bounces. - Mobile Support: Replace the keyboard controls with touch or accelerometer events.
- Power-ups: Introduce elements that change the ball's speed, paddle size, or add a second ball.
- Two-Player Mode: Let a second player control the computer paddle with the 'W' and 'S' keys.
The full source code is ready for you to experiment with. Happy coding, and welcome to the world of game development!