Welcome
M1 | The Technical Fundamentals of the Web M2 | Advanced CMS M3 | UX/UI Products M4 | Advanced Prototyping M5 | Advanced Integration & Core Project Training Bonus
About Log In Create Account
LMS Pro LMS Pro
  • Welcome
  • Web Production & Interface Training
    • Module 1 — Technical Fundamentals of the Web
    • Module 2 — Advanced CMS
    • Module 3 — Product UX & UI
    • Module 4 — Advanced Prototyping
    • Module 5 — Advanced Integration & Capstone Project
    • Bonus Module
  • About
  • Log In
  • Sign Up
Skip to content

LMS Pro

LMS Pro — Your platform to learn, grow, succeed

Professional Training

UX/UI Designer

Introduction to Arcade Game Development

Posted on 14 February 202615 February 2026 By LMS Pro No Comments on S’initier au développement de Jeux d’Arcade
Bonus Module
YouTubelongplayarcade game (video game platform)racing video game (video game genre)HD

Introduction to Arcade Game Development

Dive into the exciting world of arcade game development! Learn how to create a captivating racing game by mastering the fundamentals of interactive programming. From creating the playing field to collision mechanics, discover how to bring your game ideas to life.

Module: Module 6: Bonus Module

Level : Beginner

Duration : 25 minutes

Prerequisites: Basic concepts in HTML and CSS

Educational objectives

  • Understanding the basic structure of an arcade game in JavaScript
  • Mastering the creation of interactive visual elements with Canvas
  • Implement the player's movement and control logic
  • Develop a collision and scoring system
  • Incorporate engaging gameplay mechanics (obstacles, checkpoints)

HTML structure of the game

To create an arcade game, we need an HTML5 Canvas element to serve as the playing field. The Canvas is like a blank canvas on which we can draw in real time using JavaScript. It's the perfect element for games because it allows the display to update very quickly, creating the illusion of movement.

Initializing the graphical context

Once our canvas is created, we need to obtain its 2D context. The context is the tool that allows us to draw on the canvas—think of it as a magic paintbrush. We'll use it to draw our car, the road, obstacles, and all the visual elements of our game. The initial setup also includes defining the game's dimensions and base colors.

Key point : HTML5 Canvas is the central element of any modern web game because it offers optimal performance for real-time graphics rendering.

Key points to remember

The HTML5 Canvas and its 2D context form the technical foundation of any web arcade game, enabling high-performance graphic rendering.

🎯 Mini-exercise: Create the playing field

Create a canvas with a road and boundary lines. Observe how the 2D context allows you to draw geometric shapes.

Reference code:


<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Jeu de Course - Terrain</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background: #222;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            font-family: Arial, sans-serif;
        }
        canvas {
            border: 3px solid #fff;
            background: #333;
        }
    </style>
</head>
<body>
    <canvas id="gameCanvas" width="400" height="600"></canvas>
</body>
</html>
// Playground - Arcade Racing const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); // Terrain configuration const gameWidth = canvas.width; const gameHeight = canvas.height; const roadWidth = 300; const roadX = (gameWidth - roadWidth) / 2; function drawRoad() { // Green background (grass) ctx.fillStyle = '#4CAF50'; ctx.fillRect(0, 0, gameWidth, gameHeight); // Gray road ctx.fillStyle = '#555'; ctx.fillRect(roadX, 0, roadWidth, gameHeight); // Road edges ctx.fillStyle = '#fff'; ctx.fillRect(roadX - 5, 0, 5, gameHeight); // Left border ctx.fillRect(roadX + roadWidth, 0, 5, gameHeight); // Right border // Dotted center line ctx.fillStyle = '#ff0'; for (let y = 0; y <gameHeight; y += 40) { ctx.fillRect(roadX + roadWidth/2 - 2, y, 4, 20); } } function animate() { // Clear the canvas ctx.clearRect(0, 0, gameWidth, gameHeight); // Draw the road drawRoad(); // Game title ctx.fillStyle = '#fff'; ctx.font = '24px Arial'; ctx.textAlign = 'center'; ctx.fillText('PLAYGROUND', gameWidth/2, 50); ctx.font = '16px Arial'; ctx.fillText('Road ready for racing!', gameWidth/2, 80); requestAnimationFrame(animate); } // Start the animation animate();


Result
● Ready
Monaco Editor v0.45

Player's car design

The car is the main element with which the player interacts. We represent it as a JavaScript object containing its position (x, y), size, color, and speed. Think of the car as a video game character: it has properties (where it is, how fast it's going) and behaviors (moving left, right). To draw it, we use colored rectangles that form the body and wheels.

Keyboard control system

For the player to control their car, we need to listen for keyboard events. The left and right arrow keys will move the car horizontally. We use event listeners that react when a key is pressed or released. It's like having sensors that detect the player's actions and translate them into movements in the game.

Key point Keyboard event handling is essential to create a smooth interaction between the player and the game.

Key points to remember

The player's car is an object with properties (position, speed) and methods (movement, display) controlled by keyboard events.

🎯 Mini exercise: Controllable car

Add a car that you can move using the arrow keys. Test the left and right controls.

Reference code:


<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Jeu de Course - Contrôles</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background: #222;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            font-family: Arial, sans-serif;
        }
        .game-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 10px;
        }
        canvas {
            border: 3px solid #fff;
            background: #333;
        }
        .controls {
            color: #fff;
            text-align: center;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <canvas id="gameCanvas" width="400" height="600"></canvas>
        <div class="controls">
            <p>Use ← → to move the car</p>
        </div>
    </div>
</body>
</html>
// Controllable car - Arcade racing const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); // Game configuration const gameWidth = canvas.width; const gameHeight = canvas.height; const roadWidth = 300; const roadX = (gameWidth - roadWidth) / 2; // Player car object const player = { x: gameWidth / 2 - 15, y: gameHeight - 100, width: 30, height: 50, speed: 5, color: '#ff4444' }; // Key state const keys = { left: false, right: false }; // Handling keyboard events document.addEventListener('keydown', (e) => { switch(e.key) { case 'ArrowLeft': keys.left = true; e.preventDefault(); break; case 'ArrowRight': keys.right = true; e.preventDefault(); break; } }); document.addEventListener('keyup', (e) => { switch(e.key) { case 'ArrowLeft': keys.left = false; break; case 'ArrowRight': keys.right = false; break; } }); function updatePlayer() { // Horizontal movement if (keys.left && player.x > roadX) { player.x -= player.speed; } if (keys.right && player.x < roadX + roadWidth - player.width) { player.x += player.speed; } } function drawRoad() { // Green background (grass) ctx.fillStyle = '#4CAF50'; ctx.fillRect(0, 0, gameWidth, gameHeight); // Gray road ctx.fillStyle = '#555'; ctx.fillRect(roadX, 0, roadWidth, gameHeight); // Road edges ctx.fillStyle = '#fff'; ctx.fillRect(roadX - 5, 0, 5, gameHeight); ctx.fillRect(roadX + roadWidth, 0, 5, gameHeight); // Center line ctx.fillStyle = '#ff0'; for (let y = 0; y <gameHeight; y += 40) { ctx.fillRect(roadX + roadWidth/2 - 2, y, 4, 20); } } function drawPlayer() { // Car body ctx.fillStyle = player.color; ctx.fillRect(player.x, player.y, player.width, player.height); // Windshield ctx.fillStyle = '#87CEEB'; ctx.fillRect(player.x + 3, player.y + 5, player.width - 6, 15); // Wheels ctx.fillStyle = '#000'; ctx.fillRect(player.x - 2, player.y + 10, 6, 12); ctx.fillRect(player.x + player.width - 4, player.y + 10, 6, 12); ctx.fillRect(player.x - 2, player.y + player.height - 22, 6, 12); ctx.fillRect(player.x + player.width - 4, player.y + player.height - 22, 6, 12); } function animate() { // Update the logic updatePlayer(); // Clear and redraw ctx.clearRect(0, 0, gameWidth, gameHeight); drawRoad(); drawPlayer(); // Interface ctx.fillStyle = '#fff'; ctx.font = '16px Arial'; ctx.textAlign = 'center'; ctx.fillText('Move your car!', gameWidth/2, 30); requestAnimationFrame(animate); } // Focus to capture keyboard events canvas.focus(); canvas.tabIndex = 0; // Start the game animate();


Result
● Ready
Monaco Editor v0.45

Creation of the obstacle system

Obstacles are what make the game challenging and fun. We create them as an array of objects, each with its own position, size, and speed. The obstacles appear at the top of the screen and move down towards the player, creating the illusion that the car is traveling at high speed. It's the same principle as in old arcade games where the scenery scrolls by to simulate movement.

Automatic generation and movement

To keep the game interesting, we need to generate new obstacles regularly and randomly. A timer system triggers the creation of a new obstacle every few seconds, at a random horizontal position on the road. The obstacles move downwards with each animation frame and are removed when they go off-screen to optimize performance.

Key point Automatic obstacle management (creation, movement, removal) is crucial to maintaining optimal performance and smooth gameplay.

Key points to remember

The moving obstacles create the challenge of the game through a system of random generation, automatic movement and memory clearing.

🎯 Mini-exercise: Moving Obstacles

Implement obstacles that randomly appear at the top of the screen and move down towards your car. Observe the automatic generation.

Reference code:


<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Jeu de Course - Obstacles</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background: #222;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            font-family: Arial, sans-serif;
        }
        .game-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 10px;
        }
        canvas {
            border: 3px solid #fff;
            background: #333;
        }
        .ui {
            color: #fff;
            text-align: center;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <canvas id="gameCanvas" width="400" height="600"></canvas>
        <div class="ui">
            <p>← → to avoid obstacles</p>
            <p>Obstacles avoided: <span id="score">0</span></p>
        </div>
    </div>
</body>
</html>
// Moving Obstacles - Arcade Race const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); // Configuration const gameWidth = canvas.width; const gameHeight = canvas.height; const roadWidth = 300; const roadX = (gameWidth - roadWidth) / 2; // Player car const player = { x: gameWidth / 2 - 15, y: gameHeight - 100, width: 30, height: 50, speed: 5 }; // Obstacle system const obstacles = []; let obstacleTimer = 0; let score = 0; // Controls const keys = { left: false, right: false }; document.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft') keys.left = true; if (e.key === 'ArrowRight') keys.right = true; e.preventDefault(); }); document.addEventListener('keyup', (e) => { if (e.key === 'ArrowLeft') keys.left = false; if (e.key === 'ArrowRight') keys.right = false; }); function createObstacle() { const obstacle = { x: roadX + Math.random() * (roadWidth - 30), y: -50, width: 30, height: 50, speed: 3 + Math.random() * 2, color: `hsl(${Math.random() * 360}, 70%, 50%)` }; obstacles.push(obstacle); } function updatePlayer() { if (keys.left && player.x > roadX) { player.x -= player.speed; } if (keys.right && player.x < roadX + roadWidth - player.width) { player.x += player.speed; } } function updateObstacles() { // Create new obstacles obstacleTimer++; if (obstacleTimer > 60) { // Approximately 1 per second createObstacle(); obstacleTimer = 0; } // Move and clear obstacles for (let i = obstacles.length - 1; i >= 0; i--) { obstacles[i].y += obstacles[i].speed; // Remove if off-screen if (obstacles[i].y > gameHeight) { obstacles.splice(i, 1); score++; // Point for avoiding the obstacle document.getElementById('score').textContent = score; } } } function drawRoad() { // Grass ctx.fillStyle = '#4CAF50'; ctx.fillRect(0, 0, gameWidth, gameHeight); // Road ctx.fillStyle = '#555'; ctx.fillRect(roadX, 0, roadWidth, gameHeight); // Borders ctx.fillStyle = '#fff'; ctx.fillRect(roadX - 5, 0, 5, gameHeight); ctx.fillRect(roadX + roadWidth, 0, 5, gameHeight); // Animated center line ctx.fillStyle = '#ff0'; const lineOffset = (Date.now() * 0.1) % 40; for (let y = -lineOffset; y <gameHeight; y += 40) { ctx.fillRect(roadX + roadWidth/2 - 2, y, 4, 20); } } function drawPlayer() { // Car ctx.fillStyle = '#ff4444'; ctx.fillRect(player.x, player.y, player.width, player.height); // Details ctx.fillStyle = '#87CEEB'; ctx.fillRect(player.x + 3, player.y + 5, player.width - 6, 15); // Wheels ctx.fillStyle = '#000'; ctx.fillRect(player.x - 2, player.y + 10, 6, 12); ctx.fillRect(player.x + player.width - 4, player.y + 10, 6, 12); ctx.fillRect(player.x - 2, player.y + player.height - 22, 6, 12); ctx.fillRect(player.x + player.width - 4, player.y + player.height - 22, 6, 12); } function drawObstacles() { obstacles.forEach(obstacle => { // Obstacle body ctx.fillStyle = obstacle.color; ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height); // Windshield ctx.fillStyle = 'rgba(255,255,255,0.3)'; ctx.fillRect(obstacle.x + 3, obstacle.y + obstacle.height - 20, obstacle.width - 6, 15); // Wheels ctx.fillStyle = '#000'; ctx.fillRect(obstacle.x - 2, obstacle.y + 10, 6, 12); ctx.fillRect(obstacle.x + obstacle.width - 4, obstacle.y + 10, 6, 12); }); } function animate() { updatePlayer(); updateObstacles(); ctx.clearRect(0, 0, gameWidth, gameHeight); drawRoad(); drawObstacles(); drawPlayer(); // Interface ctx.fillStyle = '#fff'; ctx.font = '16px Arial'; ctx.textAlign = 'center'; ctx.fillText('AVOID OBSTACLES!', gameWidth/2, 30); requestAnimationFrame(animate); } // Start animate();


Result
● Ready
Monaco Editor v0.45

Rectangular collision detection

Collision detection is the system that determines when the player's car hits an obstacle. For rectangular shapes like our cars, we use the AABB (Axis-Aligned Bounding Box) algorithm. This algorithm checks if two rectangles overlap by comparing their coordinates. If the right edge of the car is to the right of the left edge of the obstacle AND the left edge of the car is to the left of the right edge of the obstacle (and the same logic applies to the top and bottom), then a collision has occurred.

Game over management

When a collision is detected, the game enters "game over" mode. We stop generating new obstacles, display a game completion message with the final score, and offer the player the option to start over. This state transition is fundamental in any game—it gives meaning to the player's actions and creates the desire to try again and do better.

Key point : AABB collision detection is simple to implement and accurate enough for most 2D arcade games.

Key points to remember

The collision system transforms the game into a real challenge with real consequences for the player's actions and a complete game cycle.

🎯 Mini-exercise: Collision and Game Over

Add collision detection and a game over screen with a restart option. Test it by intentionally touching an obstacle.

Reference code:


<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Jeu de Course - Collision</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background: #222;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            font-family: Arial, sans-serif;
        }
        .game-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 15px;
        }
        canvas {
            border: 3px solid #fff;
            background: #333;
        }
        .ui {
            color: #fff;
            text-align: center;
        }
        .stats {
            display: flex;
            gap: 20px;
            color: #fff;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <canvas id="gameCanvas" width="400" height="600"></canvas>
        <div class="stats">
            <div>Score: <span id="score">0</span></div>
            <div>Best: <span id="bestScore">0</span></div>
        </div>
        <div class="ui">
            <p>← → to avoid • SPACE to restart</p>
        </div>
    </div>
</body>
</html>
// Système de collision - Course d'arcade
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

// Configuration
const gameWidth = canvas.width;
const gameHeight = canvas.height;
const roadWidth = 300;
const roadX = (gameWidth - roadWidth) / 2;

// États du jeu
let gameState = 'playing'; // 'playing' ou 'gameOver'
let score = 0;
let bestScore = localStorage.getItem('bestScore') || 0;
document.getElementById('bestScore').textContent = bestScore;

// Voiture du joueur
const player = {
    x: gameWidth / 2 - 15,
    y: gameHeight - 100,
    width: 30,
    height: 50,
    speed: 5
};

// Obstacles
const obstacles = [];
let obstacleTimer = 0;

// Contrôles
const keys = { left: false, right: false };

document.addEventListener('keydown', (e) => {
    if (e.key === 'ArrowLeft') keys.left = true;
    if (e.key === 'ArrowRight') keys.right = true;
    if (e.key === ' ' && gameState === 'gameOver') {
        restart();
    }
    e.preventDefault();
});

document.addEventListener('keyup', (e) => {
    if (e.key === 'ArrowLeft') keys.left = false;
    if (e.key === 'ArrowRight') keys.right = false;
});

function checkCollision(rect1, rect2) {
    return rect1.x < rect2.x + rect2.width && rect1.x + rect1.width > rect2.x && rect1.y < rect2.y + rect2.height && rect1.y + rect1.height > rect2.y;
}

function restart() {
    gameState = 'playing';
    score = 0;
    document.getElementById('score').textContent = score;
    obstacles.length = 0;
    obstacleTimer = 0;
    player.x = gameWidth / 2 - 15;
    player.y = gameHeight - 100;
}

function createObstacle() {
    const obstacle = {
        x: roadX + Math.random() * (roadWidth - 30),
        y: -50,
        width: 30,
        height: 50,
        speed: 3 + Math.random() * 2,
        color: `hsl(${Math.random() * 360}, 70%, 50%)`
    };
    obstacles.push(obstacle);
}

function updateGame() {
    if (gameState !== 'playing') return;
    
    // Déplacer le joueur
    if (keys.left && player.x > roadX) {
        player.x -= player.speed;
    }
    if (keys.right && player.x < roadX + roadWidth - player.width) { player.x += player.speed; } // Handle obstacles obstacleTimer++; if (obstacleTimer > 60) { createObstacle(); obstacleTimer = 0; } // Move obstacles and check collisions for (let i = obstacles.length - 1; i >= 0; i--) { obstacles[i].y += obstacles[i].speed; // Collision with player if (checkCollision(player, obstacles[i])) { gameState = 'gameOver'; if (score > bestScore) { bestScore = score; localStorage.setItem('bestScore', bestScore); document.getElementById('bestScore').textContent = bestScore; } } // Remove if off screen if (obstacles[i].y > gameHeight) { obstacles.splice(i, 1); score++; document.getElementById('score').textContent = score; } } } function drawRoad() { // Grass ctx.fillStyle = '#4CAF50'; ctx.fillRect(0, 0, gameWidth, gameHeight); // Route ctx.fillStyle = '#555'; ctx.fillRect(roadX, 0, roadWidth, gameHeight); // Borders ctx.fillStyle = '#fff'; ctx.fillRect(roadX - 5, 0, 5, gameHeight); ctx.fillRect(roadX + roadWidth, 0, 5, gameHeight); // Animated center line ctx.fillStyle = '#ff0'; const lineOffset = gameState === 'playing' ? (Date.now() * 0.1) % 40: 0; for (let y = -lineOffset; y <gameHeight; y += 40) { ctx.fillRect(roadX + roadWidth/2 - 2, y, 4, 20); } } function drawPlayer() { // Car (red or gray if game over) ctx.fillStyle = gameState === 'playing'? '#ff4444': '#888'; ctx.fillRect(player.x, player.y, player.width, player.height); // Details ctx.fillStyle = gameState === 'playing' ? '#87CEEB': '#666'; ctx.fillRect(player.x + 3, player.y + 5, player.width - 6, 15); // Wheels ctx.fillStyle = '#000'; ctx.fillRect(player.x - 2, player.y + 10, 6, 12); ctx.fillRect(player.x + player.width - 4, player.y + 10, 6, 12); ctx.fillRect(player.x - 2, player.y + player.height - 22, 6, 12); ctx.fillRect(player.x + player.width - 4, player.y + player.height - 22, 6, 12); } function drawObstacles() { obstacles.forEach(obstacle => { ctx.fillStyle = obstacle.color; ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height); ctx.fillStyle = 'rgba(255,255,255,0.3)'; ctx.fillRect(obstacle.x + 3, obstacle.y + obstacle.height - 20, obstacle.width - 6, 15); ctx.fillStyle = '#000'; ctx.fillRect(obstacle.x - 2, obstacle.y + 10, 6, 12); ctx.fillRect(obstacle.x + obstacle.width - 4, obstacle.y + 10, 6, 12); }); } function drawUI() { ctx.fillStyle = '#fff'; ctx.font = '16px Arial'; ctx.textAlign = 'center'; if (gameState === 'playing') { ctx.fillText('AVOID OBSTACLES!', gameWidth/2, 30); } else { // Game Over screen ctx.fillStyle = 'rgba(0,0,0,0.8)'; ctx.fillRect(0, 0, gameWidth, gameHeight); ctx.fillStyle = '#ff4444'; ctx.font = '32px Arial'; ctx.fillText('GAME OVER', gameWidth/2, gameHeight/2 - 60); ctx.fillStyle = '#fff'; ctx.font = '18px Arial'; ctx.fillText(`Final score: ${score}`, gameWidth/2, gameHeight/2 - 20); if (score === bestScore && score > 0) { ctx.fillStyle = '#ff0'; ctx.fillText('🏆 NEW RECORD! 🏆', gameWidth/2, gameHeight/2 + 10); } ctx.fillStyle = '#ccc'; ctx.font = '14px Arial'; ctx.fillText('Press SPACE to start again', gameWidth/2, gameHeight/2 + 50); } } function animate() { updateGame(); ctx.clearRect(0, 0, gameWidth, gameHeight); drawRoad(); drawObstacles(); drawPlayer(); drawUI(); requestAnimationFrame(animate); } // Start animate();


Result
● Ready
Monaco Editor v0.45

Progressive level system

To keep players engaged, we've added a progressive difficulty system. The higher the score, the faster and more numerous the obstacles become. This escalating difficulty creates a natural learning curve: players improve as the game becomes more challenging. We can also add different types of obstacles with varying behaviors (lateral movement, speed changes).

Visual and audio effects

Special effects transform a functional game into an immersive experience. Explosion particles during collisions, smoke trails, and speed effects make the game more spectacular. The addition of sound effects (engine, collision, score) and background music completes the sensory experience. These elements may seem optional, but they are actually crucial for player engagement.

Key point Audiovisual improvements and difficulty progression are essential to transforming a prototype into a truly addictive game.

Key points to remember

Finalizing a game involves adding progression, visual effects, and a polished user experience to create a finished and engaging product.

🎯 Mini exercise: Full game with effects

Create the final version with progressive difficulty, particle effects, different types of obstacles and a complete user interface.

Reference code:


<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Course d'Arcade - Version Complète</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background: linear-gradient(45deg, #1a1a2e, #16213e);
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            font-family: 'Arial', sans-serif;
        }
        .game-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 15px;
        }
        canvas {
            border: 3px solid #00ff88;
            background: #0f3460;
            box-shadow: 0 0 20px rgba(0,255,136,0.3);
            border-radius: 10px;
        }
        .ui {
            color: #00ff88;
            text-align: center;
            font-weight: bold;
        }
        .stats {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 20px;
            color: #fff;
            font-weight: bold;
        }
        .stat-box {
            background: rgba(0,255,136,0.1);
            padding: 10px;
            border-radius: 8px;
            text-align: center;
            border: 1px solid rgba(0,255,136,0.3);
        }
    </style>
</head>
<body>
    <div class="game-container">
        <canvas id="gameCanvas" width="400" height="600"></canvas>
        <div class="stats">
            <div class="stat-box">
                <div>Score</div>
                <div id="score">0</div>
            </div>
            <div class="stat-box">
                <div>Level</div>
                <div id="level">1</div>
            </div>
            <div class="stat-box">
                <div>Record</div>
                <div id="bestScore">0</div>
            </div>
        </div>
        <div class="ui">
            <p>← → Navigate • SPACE Restart</p>
        </div>
    </div>
</body>
</html>
// Complete racing game - Inspired by javascript-racer const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); // Configuration const gameWidth = canvas.width; const gameHeight = canvas.height; const roadWidth = 300; const roadX = (gameWidth - roadWidth) / 2; // Game State let gameState = 'playing'; let score = 0; let level = 1; let bestScore = localStorage.getItem('racebestScore') || 0; document.getElementById('bestScore').textContent = bestScore; // Player const player = { x: gameWidth / 2 - 15, y: gameHeight - 100, width: 30, height: 50, speed: 6, trail: [] }; // Obstacles with different types const obstacles = []; const obstacleTypes = [ { color: '#ff4444', speed: 3, width: 30, height: 50 }, // Normal car { color: '#44ff44', speed: 2, width: 40, height: 60 }, // Slow truck { color: '#4444ff', speed: 5, width: 25, height: 45 }, // Fast car { color: '#ff44ff', speed: 4, width: 35, height: 55 } // Special car ]; // Particle system const particles = []; // Controls and timing const keys = { left: false, right: false }; let obstacleTimer = 0; let levelTimer = 0; // Keyboard events document.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft') keys.left = true; if (e.key === 'ArrowRight') keys.right = true; if (e.key === ' ' && gameState === 'gameOver') restart(); e.preventDefault(); }); document.addEventListener('keyup', (e) => { if (e.key === 'ArrowLeft') keys.left = false; if (e.key === 'ArrowRight') keys.right = false; }); function createParticle(x, y, color = '#ff0') { particles.push({ x: x, y: y, vx: (Math.random() - 0.5) * 4, vy: (Math.random() - 0.5) * 4, life: 30, maxLife: 30, color: color }); } function updateParticles() { for (let i = particles.length - 1; i >= 0; i--) { const p = particles[i]; px += p.vx; py += p.vy; p.life--; p.vy += 0.1; // Severity if (p.life <= 0) { particles.splice(i, 1); } } } function drawParticles() { particles.forEach(p => { const alpha = p.life / p.maxLife; ctx.save(); ctx.globalAlpha = alpha; ctx.fillStyle = p.color; ctx.fillRect(px, py, 3, 3); ctx.restore(); }); } function checkCollision(rect1, rect2) { return rect1.x < rect2.x + rect2.width && rect1.x + rect1.width > rect2.x && rect1.y < rect2.y + rect2.height && rect1.y + rect1.height > rect2.y;
}

function restart() {
    gameState = 'playing';
    score = 0;
    level = 1;
    obstacles.length = 0;
    particles.length = 0;
    obstacleTimer = 0;
    levelTimer = 0;
    player.x = gameWidth / 2 - 15;
    player.trail.length = 0;
    updateUI();
}

function updateUI() {
    document.getElementById('score').textContent = score;
    document.getElementById('level').textContent = level;
    if (score > bestScore) {
        bestScore = score;
        localStorage.setItem('racebestScore', bestScore);
        document.getElementById('bestScore').textContent = bestScore;
    }
}

function createObstacle() {
    const type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
    const obstacle = {
        x: roadX + Math.random() * (roadWidth - type.width),
        y: -type.height,
        width: type.width,
        height: type.height,
        speed: type.speed + level * 0.5,
        color: type.color
    };
    obstacles.push(obstacle);
}

function updateGame() {
    if (gameState !== 'playing') return;
    
    // Progression de niveau
    levelTimer++;
    if (levelTimer > 1800) { // 30 secondes
        level++;
        levelTimer = 0;
        // Effets de niveau
        for (let i = 0; i <10; i++) { createParticle(gameWidth/2 + Math.random() * 100 - 50, 100, '#00ff88'); } } // Player Trail player.trail.unshift({ x: player.x + player.width/2, y: player.y + player.height }); if (player.trail.length > 8) player.trail.pop(); // Player movement if (keys.left && player.x > roadX) { player.x -= player.speed; createParticle(player.x + player.width, player.y + player.height, '#88aaff'); } if (keys.right && player.x < roadX + roadWidth - player.width) { player.x += player.speed; createParticle(player.x, player.y + player.height, '#88aaff'); } // Obstacle generation (more frequent at higher levels) obstacleTimer++; const spawnRate = Math.max(40 - level * 3, 20); if (obstacleTimer > spawnRate) { createObstacle(); obstacleTimer = 0; } // Update obstacles for (let i = obstacles.length - 1; i >= 0; i--) { obstacles[i].y += obstacles[i].speed; // Collision if (checkCollision(player, obstacles[i])) { gameState = 'gameOver'; // Explosion for (let j = 0; j <20; j++) { createParticle( player.x + Math.random() * player.width, player.y + Math.random() * player.height, '#ff4444' ); } updateUI(); } // Cleanup if (obstacles[i].y > gameHeight) { obstacles.splice(i, 1); score += 10; updateUI(); } } updateParticles(); } function drawRoad() { // Background gradient const gradient = ctx.createLinearGradient(0, 0, gameWidth, 0); gradient.addColorStop(0, '#2a4d3a'); gradient.addColorStop(0.5, '#1e3a1e'); gradient.addColorStop(1, '#2a4d3a'); ctx.fillStyle = gradient; ctx.fillRect(0, 0, gameWidth, gameHeight); // Road with gradient const roadGradient = ctx.createLinearGradient(roadX, 0, roadX + roadWidth, 0); roadGradient.addColorStop(0, '#333'); roadGradient.addColorStop(0.5, '#666'); roadGradient.addColorStop(1, '#333'); ctx.fillStyle = roadGradient; ctx.fillRect(roadX, 0, roadWidth, gameHeight); // Glossy borders ctx.fillStyle = '#00ff88'; ctx.fillRect(roadX - 3, 0, 3, gameHeight); ctx.fillRect(roadX + roadWidth, 0, 3, gameHeight); // Animated center line ctx.fillStyle = '#ffff00'; const speed = gameState === 'playing' ? 0.2 * (1 + level * 0.1): 0; const lineOffset = (Date.now() * speed) % 40; for (let y = -lineOffset; y <gameHeight; y += 40) { ctx.fillRect(roadX + roadWidth/2 - 2, y, 4, 20); } } function drawPlayer() { // Trail ctx.strokeStyle = '#88aaff'; ctx.lineWidth = 3; ctx.beginPath(); player.trail.forEach((point, i) => { ctx.globalAlpha = (player.trail.length - i) / player.trail.length * 0.5; if (i === 0) ctx.moveTo(point.x, point.y); else ctx.lineTo(point.x, point.y); }); ctx.stroke(); ctx.globalAlpha = 1; // Car ctx.fillStyle = gameState === 'playing' ? '#ff4444': '#666'; ctx.fillRect(player.x, player.y, player.width, player.height); // Details with shine ctx.fillStyle = gameState === 'playing' ? '#87CEEB': '#444'; ctx.fillRect(player.x + 3, player.y + 5, player.width - 6, 15); // Wheels with reflections ctx.fillStyle = '#000'; ctx.fillRect(player.x - 2, player.y + 10, 6, 12); ctx.fillRect(player.x + player.width - 4, player.y + 10, 6, 12); ctx.fillRect(player.x - 2, player.y + player.height - 22, 6, 12); ctx.fillRect(player.x + player.width - 4, player.y + player.height - 22, 6, 12); ctx.fillStyle = '#333'; ctx.fillRect(player.x, player.y + 12, 4, 8); ctx.fillRect(player.x + player.width - 4, player.y + 12, 4, 8); } function drawObstacles() { obstacles.forEach(obstacle => { ctx.fillStyle = obstacle.color; ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height); // Reflection ctx.fillStyle = 'rgba(255,255,255,0.3)'; ctx.fillRect(obstacle.x + 3, obstacle.y + 5, obstacle.width - 6, 10); // Wheels ctx.fillStyle = '#000'; ctx.fillRect(obstacle.x - 2, obstacle.y + 10, 6, 12); } function drawUI() { if (gameState === 'playing') { // Visual speed ctx.fillStyle = '#00ff88'; ctx.font = 'bold 20px Arial'; ctx.textAlign = 'center'; ctx.fillText('LEVEL ' + level, gameWidth/2, 35); // Level progress bar const progress = (levelTimer / 1800) * 200; ctx.fillStyle = 'rgba(0,255,136,0.3)'; ctx.fillRect(gameWidth/2 - 100, 45, 200, 8); ctx.fillStyle = '#00ff88'; ctx.fillRect(gameWidth/2 - 100, 45, progress, 8); } else { // Game Over elaborated ctx.fillStyle = 'rgba(0,0,0,0.8)'; ctx.fillRect(0, 0, gameWidth, gameHeight); ctx.fillStyle = '#ff4444'; ctx.font = 'bold 32px Arial'; ctx.textAlign = 'center'; ctx.fillText('CRASH!', gameWidth/2, gameHeight/2 - 80); ctx.fillStyle = '#fff'; ctx.font = '18px Arial'; ctx.fillText(`Score: ${score}`, gameWidth/2, gameHeight/2 - 40); ctx.fillText(`Level reached: ${level}`, gameWidth/2, gameHeight/2 - 10); if (score === parseInt(bestScore) && score > 0) { ctx.fillStyle = '#00ff88'; ctx.font = 'bold 16px Arial'; ctx.fillText('🏆 NEW RECORD! 🏆', gameWidth/2, gameHeight/2 + 20); } ctx.fillStyle = '#ccc'; ctx.font = '14px Arial'; ctx.fillText('SPACE to start again', gameWidth/2, gameHeight/2 + 60); } } function animate() { updateGame(); ctx.clearRect(0, 0, gameWidth, gameHeight); drawRoad(); drawObstacles(); drawPlayer(); drawParticles(); drawUI(); requestAnimationFrame(animate); } // Start the game animate();


Result
● Ready
Monaco Editor v0.45

Summary

Key points to remember

  • Canvas HTML5 A fundamental element for creating web games with high-performance graphics rendering
  • Game Loop : Update-display cycle that enables smooth animation and game logic
  • Event Management Essential keyboard control system for player interactivity
  • Collision detection AABB algorithm for managing interactions between game objects
  • Progression and effects A system of levels and particles that transform a prototype into an engaging game

Sources

To deepen your knowledge:


  • 🔗
    MDN Canvas API - Complete Guide to Game Development
    →

  • 🔗
    JavaScript Game Development Tutorials - Advanced Techniques
    →

Validate your knowledge

Test your understanding with this 10-question quiz:

Loading quiz...
Categories: Bonus Design UX

Post navigation

❮ Previous Post: Getting Started with SaaS SCSS
Next Post: Learn how to generate abstract and geometric wallpapers ❯

See also

Bonus Module
Learn how to generate abstract and geometric wallpapers
February 14, 2026
Bonus Module
premiers pas sur Python
28 February 2026

LMS Pro LMS Pro is an educational platform dedicated to Professional Training. LMS Pro 2026 All rights reserved

You must log in

Forgot password?
No account yet., click here
LMS Pro

Create an account

Fill in this information

Already have an account? Log in
LMS Pro
English
French