🎮ArcadeLab

Flappy Bird Game

by RocketTurtle68
358 lines6.6 KB
▶ Play
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flappy Bird Game</title>

<style>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: Arial, sans-serif;
    background: #172554;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    overflow: hidden;
}

.game-container {
    text-align: center;
    width: 100%;
    max-width: 420px;
    padding: 10px;
}

h1 {
    color: white;
    margin-bottom: 10px;
}

canvas {
    display: block;
    width: 100%;
    max-width: 360px;
    height: auto;
    margin: auto;
    border: 4px solid white;
    border-radius: 12px;
    background: #70c5ce;
    touch-action: manipulation;
}

button {
    margin-top: 12px;
    padding: 12px 25px;
    border: none;
    border-radius: 8px;
    background: #facc15;
    color: #172554;
    font-size: 18px;
    font-weight: bold;
    cursor: pointer;
}

p {
    color: white;
    margin-top: 8px;
}
</style>
</head>

<body>

<div class="game-container">
    <h1>🐦 Flappy Bird</h1>

    <canvas id="game" width="360" height="520"></canvas>

    <button id="startBtn">Start Game</button>

    <p>Tap / Click / Press Space to Fly</p>
</div>

<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const startBtn = document.getElementById("startBtn");

const W = canvas.width;
const H = canvas.height;
const ground = 475;

let bird, pipes, score, frame;
let state = "ready";
let animationId;

function resetGame() {
    bird = {
        x: 85,
        y: 220,
        radius: 14,
        velocity: 0
    };

    pipes = [];
    score = 0;
    frame = 0;
}

function drawBird() {
    ctx.save();
    ctx.translate(bird.x, bird.y);

    // Body
    ctx.fillStyle = "#ffd43b";
    ctx.beginPath();
    ctx.arc(0, 0, bird.radius, 0, Math.PI * 2);
    ctx.fill();

    // Wing
    ctx.fillStyle = "#f59e0b";
    ctx.beginPath();
    ctx.ellipse(-5, 4, 8, 5, -0.3, 0, Math.PI * 2);
    ctx.fill();

    // Eye
    ctx.fillStyle = "white";
    ctx.beginPath();
    ctx.arc(5, -5, 5, 0, Math.PI * 2);
    ctx.fill();

    ctx.fillStyle = "black";
    ctx.beginPath();
    ctx.arc(7, -5, 2, 0, Math.PI * 2);
    ctx.fill();

    // Beak
    ctx.fillStyle = "#f97316";
    ctx.beginPath();
    ctx.moveTo(11, 1);
    ctx.lineTo(23, 5);
    ctx.lineTo(11, 8);
    ctx.closePath();
    ctx.fill();

    ctx.restore();
}

function drawBackground() {
    // Sky
    ctx.fillStyle = "#70c5ce";
    ctx.fillRect(0, 0, W, H);

    // Clouds
    ctx.fillStyle = "rgba(255,255,255,0.75)";

    [[60, 90], [245, 125], [160, 55]].forEach(([x, y]) => {
        ctx.beginPath();
        ctx.arc(x, y, 17, 0, Math.PI * 2);
        ctx.arc(x + 20, y - 8, 22, 0, Math.PI * 2);
        ctx.arc(x + 42, y, 16, 0, Math.PI * 2);
        ctx.fill();
    });

    // Ground
    ctx.fillStyle = "#ded895";
    ctx.fillRect(0, ground, W, H - ground);

    ctx.fillStyle = "#70bf44";
    ctx.fillRect(0, ground, W, 12);

    ctx.fillStyle = "#b6a46b";

    for (let x = 0; x < W; x += 28) {
        ctx.fillRect(x, ground + 20, 15, 4);
    }
}

function drawPipes() {
    pipes.forEach(pipe => {
        const bottomY = pipe.top + pipe.gap;

        // Upper pipe
        ctx.fillStyle = "#16a34a";
        ctx.fillRect(pipe.x, 0, pipe.width, pipe.top);

        ctx.fillStyle = "#22c55e";
        ctx.fillRect(pipe.x - 4, pipe.top - 16, pipe.width + 8, 16);

        // Lower pipe
        ctx.fillStyle = "#16a34a";
        ctx.fillRect(pipe.x, bottomY, pipe.width, ground - bottomY);

        ctx.fillStyle = "#22c55e";
        ctx.fillRect(pipe.x - 4, bottomY, pipe.width + 8, 16);
    });
}

function drawScore() {
    ctx.textAlign = "center";
    ctx.font = "bold 38px Arial";
    ctx.lineWidth = 4;
    ctx.strokeStyle = "#333";
    ctx.strokeText(score, W / 2, 55);
    ctx.fillStyle = "white";
    ctx.fillText(score, W / 2, 55);
}

function drawMessage(title, subtitle) {
    ctx.fillStyle = "rgba(0,0,0,0.55)";
    ctx.fillRect(25, 175, W - 50, 120);

    ctx.fillStyle = "white";
    ctx.textAlign = "center";
    ctx.font = "bold 27px Arial";
    ctx.fillText(title, W / 2, 220);

    ctx.font = "15px Arial";
    ctx.fillText(subtitle, W / 2, 255);
}

function addPipe() {
    const gap = 145;
    const margin = 65;
    const top = margin + Math.random() *
        (ground - gap - margin * 2);

    pipes.push({
        x: W,
        width: 48,
        top: top,
        gap: gap,
        scored: false
    });
}

function collision(pipe) {
    const r = bird.radius;

    if (bird.y - r < 0 || bird.y + r > ground) {
        return true;
    }

    const overlapsX =
        bird.x + r > pipe.x &&
        bird.x - r < pipe.x + pipe.width;

    const hitsPipe =
        bird.y - r < pipe.top ||
        bird.y + r > pipe.top + pipe.gap;

    return overlapsX && hitsPipe;
}

function flap() {
    if (state === "ready") {
        startGame();
        bird.velocity = -6.5;
        return;
    }

    if (state === "over") {
        return;
    }

    bird.velocity = -6.5;
}

function startGame() {
    cancelAnimationFrame(animationId);

    resetGame();
    state = "playing";
    startBtn.textContent = "Restart Game";

    loop();
}

function gameOver() {
    state = "over";
    startBtn.textContent = "Play Again";
}

function update() {
    frame++;

    bird.velocity += 0.32;
    bird.y += bird.velocity;

    if (frame % 95 === 0) {
        addPipe();
    }

    pipes.forEach(pipe => {
        pipe.x -= 2.4;

        if (!pipe.scored && pipe.x + pipe.width < bird.x) {
            pipe.scored = true;
            score++;
        }

        if (collision(pipe)) {
            gameOver();
        }
    });

    pipes = pipes.filter(pipe => pipe.x + pipe.width > 0);

    if (bird.y + bird.radius >= ground ||
        bird.y - bird.radius <= 0) {
        gameOver();
    }
}

function render() {
    drawBackground();
    drawPipes();
    drawBird();
    drawScore();

    if (state === "ready") {
        drawMessage("FLAPPY BIRD", "Tap to start flying!");
    }

    if (state === "over") {
        drawMessage("GAME OVER", "Score: " + score);
    }
}

function loop() {
    if (state !== "playing") {
        render();
        return;
    }

    update();
    render();

    if (state === "playing") {
        animationId = requestAnimationFrame(loop);
    }
}

canvas.addEventListener("pointerdown", flap);

document.addEventListener("keydown", event => {
    if (event.code === "Space" && !event.repeat) {
        event.preventDefault();
        flap();
    }
});

startBtn.addEventListener("click", () => {
    startGame();
});

resetGame();
render();
</script>

</body>
</html>

Game Source: Flappy Bird Game

Creator: RocketTurtle68

Libraries: none

Complexity: complex (358 lines, 6.6 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: flappy-bird-game-rocketturtle68" to link back to the original. Then publish at arcadelab.ai/publish.