Flappy Bird Game
by RocketTurtle68358 lines6.6 KB
<!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.