Building Flappy Bird on the Web

Flappy Bird games are quite popular and surprisingly simple to make. You might have played one or seen someone trying to beat their high score. These games are a fun way to learn about web development using HTML, CSS, and JavaScript.

I recently worked on a project to create a Flappy Bird clone for educational purposes. The idea was to understand how different parts of web development come together to make an interactive game.

I wanted something reusable, so others could learn from it or modify it for their projects. Before diving into the code, let's ensure we understand how such a game works.

If you're eager to see the full code and skip the explanations, you can find the complete project on GitHub.

What Is a Flappy Bird Game?

If for some strange reason, you do not know what Flappy Bird is, it is a game that involves guiding a bird through gaps between pipes by making it flap to stay airborne. The bird moves forward automatically, and the player controls its vertical movement. The goal is to avoid crashing into the pipes or falling to the ground.

You've probably seen similar games used to:

  • Teach programming concepts

  • Demonstrate game physics

  • Create fun challenges for players

The game combines graphics, user input, and real-time updates to create an engaging experience.

Why Create a Flappy Bird Game?

While there are many versions of Flappy Bird available, creating your own allows you to:

  • Learn how HTML, CSS, and JavaScript work together

  • Understand game physics like gravity and collision detection

  • Customize the game to your liking

  • Share it with friends or use it as a learning tool

By building the game from scratch, you gain valuable coding experience and have a project you can expand upon.

Tools and Files Needed for This Project

To create the game, we'll use three main files:

  1. index.html: The structure of the game.

  2. style.css: The styling and appearance.

  3. game.js: The logic and interactivity.

You'll need a basic text editor and a web browser to run the game. I use VS Code with Live Server.

Understanding the Basics of the Game

Before we start coding, let's break down how the game works:

  • The Bird: Moves up and down based on player input and gravity.

  • The Pipes: Move towards the bird, and the player must navigate through them.

  • Scoring: Increases as the bird passes through pipes.

  • Game Over: Occurs when the bird hits a pipe or the ground.

Setting Up the Game with HTML

Our game begins with an HTML file. This file tells the computer what to show on the screen.

The Basic Structure

At the top of our HTML file, we have:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flappy Bird Clone</title>
    <link rel="stylesheet" href="style/style.css">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <!-- Game content will go here -->
</body>
</html>
  • <!DOCTYPE html> tells the browser we are using HTML5.

  • <html lang="en"> starts the HTML document in English.

  • Inside <head>, we have:

    • <meta charset="UTF-8"> to handle text correctly.

    • <title>Flappy Bird Clone</title> which sets the page title.

    • <link rel="stylesheet" href="style/style.css"> links to our CSS file.

    • <meta name="viewport" content="width=device-width, initial-scale=1.0"\> ensures the game looks good on all devices.

Creating the Game Area

Inside the <body>, we set up the game area:

<div id="gameContainer">
    <canvas id="gameCanvas" tabindex="0"></canvas>
    <!-- Other content will go here -->
</div>
  • <div id="gameContainer"> is a box that holds our game.

  • <canvas id="gameCanvas" tabindex="0"></canvas> is where we draw the game.

    • The id="game canvas" lets us find the canvas in our JavaScript code.

    • The tabindex="0" allows the canvas to receive keyboard input.

Adding Start and Game Over Screens

We include screens for starting the game and when the game is over:

<!-- Start Screen -->
<div id="startScreen">
    <button id="startButton">Start Game</button>
</div>

<!-- Game Over Screen -->
<div id="gameOverScreen" style="display: none;">
    <h1>Game Over!</h1>
    <p>Your score: <span id="finalScore"></span></p>
    <button id="restartButton">Play Again</button>
</div>
  • <div id="startScreen"> shows the "Start Game" button before the game begins.

  • <div id="gameOverScreen" style="display: none;"> is hidden at first and shows up when the game ends.

Linking the JavaScript File

At the end of the body, we link our JavaScript file:

<script src="script/game.js"></script>
  • This script will make our game work by controlling the game logic.

Styling the Game with CSS

Our CSS file makes the game look nice.

Styling the Body

We start by styling the body of the page:

body {
    margin: 0;
    overflow: hidden;
    background-color: #70c5ce;
    position: relative;
}
  • margin: 0; removes any space around the page.

  • overflow: hidden; hides anything that goes outside the page edges.

  • background-color: #70c5ce; sets a sky-blue background.

  • position: relative; allows us to position other elements inside the body.

Styling the Game Container

We center the game container:

#gameContainer {
    width: 100%;
    max-width: 400px;
    margin: 0 auto;
    position: relative;
}
  • width: 100%; makes it fill the screen width.

  • max-width: 400px; limits the width on larger screens.

  • margin: 0 auto; centers the container horizontally.

Styling the Canvas

We make the canvas fill the container:

canvas {
    display: block;
    width: 100%;
    background-color: #70c5ce;
}
  • display: block; treats the canvas like a block element.

  • width: 100%; makes it fill the container.

  • background-color: #70c5ce; matches the body's background.

Styling the Overlays

We style the start and game over screens:

#startScreen,
#gameOverScreen {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background-color: rgba(0, 0, 0, 0.7);
    color: #fff;
}
  • position: absolute; places them over the game area.

  • display: flex; centers the content inside.

  • background-color: rgba(0, 0, 0, 0.7); gives a transparent dark background.

  • color: #fff; sets the text color to white.

Styling the Buttons

We make the buttons easy to click:

#startButton,
#restartButton {
    padding: 10px 20px;
    font-size: 20px;
    cursor: pointer;
}
  • padding: 10px 20px; adds space inside the buttons.

  • font-size: 20px; makes the text larger.

  • cursor: pointer; changes the cursor to a hand when hovering.


Making the Game Work with JavaScript

Now, we will look at the JavaScript code that makes the game interactive.

Setting Up the Canvas

We start by getting the canvas and setting up the drawing context:

const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
  • canvas refers to our game area.

  • ctx is used to draw on the canvas.

Declaring Game Variables

We declare variables to keep track of the game state:

let birdX, birdY, birdRadius;
let gravity, lift, velocity;
let score;
let pipes;
let pipeWidth, pipeGap;
let frameCount;
let gameOver;
let gameStarted = false;
  • Bird Variables: Position and size of the bird.

  • Physics Variables: How the bird moves.

  • Game Variables: Score, pipes, and game state.

Adjusting the Canvas Size

We make the game responsive:

function resizeCanvas() {
    if (window.innerWidth <= 768) {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    } else {
        canvas.width = 400;
        canvas.height = 600;
    }
    if (!gameStarted) {
        init();
    }
}
  • On mobile devices, the game fills the screen.

  • On desktops, it has fixed dimensions.

  • We call init() to reset variables if the game hasn't started.

We listen for window resize events:

window.addEventListener('resize', () => {
    resizeCanvas();
});

Initializing the Game

We set up the initial game state:

function init() {
    birdX = canvas.width * 0.1;
    birdY = canvas.height / 2;
    birdRadius = canvas.width * 0.03;
    gravity = canvas.height * 0.00015;
    lift = -canvas.height * 0.007;
    velocity = 0;
    score = 0;
    pipes = [];
    pipeWidth = canvas.width * 0.15;
    pipeGap = canvas.height * 0.35;
    frameCount = 0;
    gameOver = false;
}
  • We calculate values based on the canvas size to keep things proportional.

Handling User Input

We make the bird fly when we click or tap:

function handleInput() {
    if (!gameOver) {
        velocity = lift;
    }
}

canvas.addEventListener('mousedown', handleInput);
canvas.addEventListener('touchstart', function(e) {
    e.preventDefault();
    handleInput();
}, { passive: false });
  • handleInput() makes the bird go up by setting its velocity.

  • We prevent the default touch behavior to avoid scrolling on mobile devices.

Creating the Pipes

We define a Pipe class:

class Pipe {
    constructor() {
        const minPipeHeight = canvas.height * 0.1;
        const maxPipeHeight = canvas.height * 0.6;
        this.top = Math.random() * (maxPipeHeight - minPipeHeight) + minPipeHeight;
        this.bottom = canvas.height - (this.top + pipeGap);
        this.x = canvas.width;
        this.width = pipeWidth;
        this.speed = canvas.width * 0.002;
        this.passed = false;
    }

    draw() {
        ctx.fillStyle = '#228B22';
        ctx.fillRect(this.x, 0, this.width, this.top);
        ctx.fillRect(this.x, canvas.height - this.bottom, this.width, this.bottom);
    }

    update() {
        this.x -= this.speed;
        this.draw();
    }
}
  • constructor() sets up a new pipe with random heights.

  • draw() draws the pipe on the canvas.

  • update() moves the pipe to the left and redraws it.

The Game Loop

We create the main game loop:

function animate() {
    if (gameOver) return;
    requestAnimationFrame(animate);
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Move and draw the bird
    // Handle pipes
    // Check for collisions
    // Update score
}
  • requestAnimationFrame(animate) keeps the game running smoothly.

  • We clear the canvas each frame to redraw everything.

Moving the Bird

We update the bird's position:

velocity += gravity;
birdY += velocity;
ctx.fillStyle = '#FF0000';
ctx.beginPath();
ctx.arc(birdX, birdY, birdRadius, 0, Math.PI * 2);
ctx.fill();
  • velocity += gravity; makes the bird fall.

  • birdY += velocity; updates the bird's vertical position.

  • We draw the bird as a red circle.

Handling the Pipes

We manage the pipes:

if (frameCount % Math.floor(canvas.width * 0.5) === 0) {
    pipes.push(new Pipe());
}

pipes.forEach((pipe, index) => {
    pipe.update();

    // Collision Detection
    if (
        birdX + birdRadius > pipe.x &&
        birdX - birdRadius < pipe.x + pipe.width &&
        (birdY - birdRadius < pipe.top || birdY + birdRadius > canvas.height - pipe.bottom)
    ) {
        gameOver = true;
        endGame();
    }

    // Scoring
    if (pipe.x + pipe.width < birdX - birdRadius && !pipe.passed) {
        score++;
        pipe.passed = true;
    }

    // Remove off-screen pipes
    if (pipe.x + pipe.width < 0) {
        pipes.splice(index, 1);
    }
});
  • We add new pipes at intervals.

  • We move and draw each pipe.

  • We check if the bird hits a pipe.

  • We increase the score when passing a pipe.

  • We remove pipes that have left the screen.

Drawing the Score

We display the score:

ctx.fillStyle = '#000';
ctx.font = `${canvas.width * 0.05}px Arial`;
ctx.fillText('Score: ' + score, canvas.width * 0.05, canvas.height * 0.1);
  • The font size adjusts based on the canvas width.

  • We draw the score in the top-left corner.

Checking for Game Over

We check if the bird hits the ground or flies off the top:

if (birdY + birdRadius > canvas.height || birdY - birdRadius < 0) {
    gameOver = true;
    endGame();
}
  • If the bird goes off the screen, we end the game.

Updating the Frame Count

We keep track of frames:

frameCount++;
  • This helps us control when to add new pipes.

Starting and Restarting the Game

We listen for button clicks:

document.getElementById('startButton').addEventListener('click', () => {
    document.getElementById('startScreen').style.display = 'none';
    canvas.focus();
    gameStarted = true;
    init();
    animate();
});

document.getElementById('restartButton').addEventListener('click', () => {
    document.getElementById('gameOverScreen').style.display = 'none';
    init();
    animate();
});
  • We hide the start or game over screens.

  • We initialize the game and start the animation loop.

Ending the Game

We define the endGame() function:

function endGame() {
    document.getElementById('finalScore').innerText = score;
    document.getElementById('gameOverScreen').style.display = 'flex';
    gameStarted = false;
}
  • We display the game over screen with the final score.

  • We stop the game from running.


How Everything Works Together

  • HTML creates the structure of the game.

  • CSS styles the game elements.

  • JavaScript controls the game's behavior.

When you click "Start Game":

  • The start screen hides.

  • The game variables initialize.

  • The animate() function starts running.

During the game:

  • The bird moves up when you click or tap.

  • Gravity pulls the bird down.

  • Pipes move from right to left.

  • You score points by passing through pipes.

  • The game checks for collisions.

When the game ends:

  • The game over screen shows up.

  • You see your final score.

  • You can click "Play Again" to restart.


This is how to build a simple Flappy Bird game. We used HTML to set up the game area, CSS to style it, and JavaScript to make it work. By understanding each part of the code, we see how the game comes together. Now, you can try making your own game or adding new features to this one.