Prompt
Create a 1:1 flappy bird clone website
A system prompt was added to support web rendering
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Flappy Bird Clone</title> <style> html,body{ height:100%; margin:0; } body{ display:flex; justify-content:center; align-items:center; background:#87CEEB; /* sky-blue page background */ font-family:sans-serif; user-select:none; } canvas{ background:#70c5ce; /* in-game sky */ box-shadow:0 0 10px rgba(0,0,0,.5); image-rendering:pixelated; cursor:pointer; } #overlay{ position:absolute; top:0; left:0; width:100%; height:100%; display:flex; flex-direction:column; justify-content:center; align-items:center; pointer-events:none; /* let the canvas keep input focus */ color:#fff; text-shadow:0 0 5px #000; } #overlay button{ margin-top:12px; padding:8px 20px; font-size:18px; border:none; border-radius:4px; background:#ffb000; color:#fff; cursor:pointer; pointer-events:auto; /* but the button itself is clickable */ } </style> </head> <body> <canvas id="game" width="288" height="512"></canvas> <!-- centred overlay for hints / game-over screen --> <div id="overlay" style="display:none;"> <h1 id="message">Tap / Press SPACE to flap</h1> <button id="restart">Restart</button> </div> <script> /* Basic 1-to-1 Flappy Bird mechanics implemented with vanilla JS & Canvas. No external assets – everything is drawn as vectors so the file is self-contained. */ const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); const W = canvas.width; const H = canvas.height; const GROUND_H = 112; // ground strip height const PIPE_W = 52; const GAP = 100; // vertical space between pipes const PIPE_SPEED = 2; // horizontal scrolling speed const GRAVITY = 0.5; const FLAP_POWER = -8; const MAX_DROP = 12; const SPAWN_MS = 1500; // new pipe every 1.5s /* Game state -------------------------------------------------------------- */ let pipes = []; // active pipe pairs let lastTime = 0; let spawnTimer = 0; let score = 0; let bestScore = 0; const bird = { x: W * 0.22, y: H / 2, vy: 0, r : 12, // radius alive: true, flap(){ this.vy = FLAP_POWER; } }; /* Overlay helpers --------------------------------------------------------- */ const overlay = document.getElementById('overlay'); const message = document.getElementById('message'); const restart = document.getElementById('restart'); function showOverlay(txt){ message.innerHTML = txt; overlay.style.display = 'flex'; } function hideOverlay(){ overlay.style.display = 'none'; } /* Pipe factory ------------------------------------------------------------ */ function spawnPipe(){ // Pick random hole position leaving room at top & bottom. const topH = Math.random() * (H - GROUND_H - GAP - 40) + 20; pipes.push({ x : W, topEnd : topH, // bottom edge of the top pipe botY : topH + GAP, // y-start for bottom pipe scored : false }); } /* Reset whole game -------------------------------------------------------- */ function reset(){ pipes.length = 0; bird.y = H / 2; bird.vy = 0; bird.alive = true; score = 0; spawnTimer = 0; hideOverlay(); lastTime = performance.now(); requestAnimationFrame(loop); } /* Input: space bar & pointer --------------------------------------------- */ function userFlap(){ bird.alive ? bird.flap() : reset(); } window.addEventListener('keydown', e=>{ if(e.code === 'Space'){ e.preventDefault(); userFlap(); } }); ['mousedown','touchstart'].forEach(evt=>{ window.addEventListener(evt, e=>{ e.preventDefault(); userFlap(); }); }); restart.addEventListener('click', reset); /* Drawing helpers --------------------------------------------------------- */ function drawBG(){ ctx.fillStyle = '#70c5ce'; ctx.fillRect(0,0,W,H); } function drawGround(){ ctx.fillStyle = '#ded895'; ctx.fillRect(0, H-GROUND_H, W, GROUND_H); } function drawPipes(){ ctx.fillStyle = '#008000'; pipes.forEach(p=>{ // top pipe ctx.fillRect(p.x, 0, PIPE_W, p.topEnd); // bottom pipe ctx.fillRect(p.x, p.botY, PIPE_W, H - GROUND_H - p.botY); }); } function drawBird(){ ctx.save(); ctx.translate(bird.x, bird.y); ctx.rotate(bird.vy / 10); // tilt based on speed ctx.fillStyle = '#ffd800'; ctx.beginPath(); ctx.arc(0,0,bird.r,0,Math.PI*2); ctx.fill(); ctx.fillStyle = '#ff0000'; // eye ctx.beginPath(); ctx.arc(bird.r/2, -bird.r/2, 3, 0, Math.PI*2); ctx.fill(); ctx.restore(); } function drawScore(){ ctx.fillStyle = '#fff'; ctx.font = '30px monospace'; ctx.textAlign = 'center'; ctx.fillText(score, W/2, 50); } /* Update simulation ------------------------------------------------------- */ function update(dt){ if(!bird.alive) return; /* bird physics */ bird.vy = Math.min(bird.vy + GRAVITY, MAX_DROP); bird.y += bird.vy; /* spawn pipes */ spawnTimer += dt; if(spawnTimer > SPAWN_MS){ spawnPipe(); spawnTimer = 0; } /* move pipes & check collision/score */ for(let i=pipes.length-1; i>=0; i--){ const p = pipes[i]; p.x -= PIPE_SPEED; /* scoring: passed centre of pipe */ if(!p.scored && p.x + PIPE_W < bird.x){ p.scored = true; score++; if(score > bestScore) bestScore = score; } /* collision */ const withinX = bird.x + bird.r > p.x && bird.x - bird.r < p.x + PIPE_W; if(withinX){ const hitTop = bird.y - bird.r < p.topEnd; const hitBot = bird.y + bird.r > p.botY; if(hitTop || hitBot) bird.alive = false; } /* purge off-screen pipes */ if(p.x + PIPE_W < 0) pipes.splice(i,1); } /* hit ground / ceiling */ if(bird.y + bird.r > H - GROUND_H || bird.y - bird.r < 0){ bird.alive = false; } } /* Main loop --------------------------------------------------------------- */ function loop(ts){ const dt = ts - lastTime; lastTime = ts; update(dt); drawBG(); drawPipes(); drawGround(); drawBird(); drawScore(); if(bird.alive){ requestAnimationFrame(loop); }else{ showOverlay(`Game Over<br>Score ${score}<br>Best ${bestScore}`); } } /* Kick things off --------------------------------------------------------- */ reset(); </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Flappy Bird Clone</title> <style> body { background-color: #333; color: #fff; font-family: 'Helvetica Neue', Arial, sans-serif; text-align: center; margin: 0; padding: 0; overflow: hidden; /* Hide scrollbars */ display: flex; justify-content: center; align-items: center; height: 100vh; } canvas { border: 2px solid #000; background-color: #70c5ce; display: block; } </style> </head> <body> <canvas id="gameCanvas" width="320" height="480"></canvas> <script> const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); // GAME STATE let frames = 0; const state = { current: 0, getReady: 0, game: 1, over: 2 }; // CONTROL function handleInput(evt) { switch (state.current) { case state.getReady: state.current = state.game; break; case state.game: bird.flap(); break; case state.over: let rect = canvas.getBoundingClientRect(); let clickX = evt.clientX - rect.left; let clickY = evt.clientY - rect.top; // Check if click is on restart button if (clickX >= 110 && clickX <= 110 + 100 && clickY >= 263 && clickY <= 263 + 36) { resetGame(); } break; } } document.addEventListener('keydown', function(evt) { if (evt.code === 'Space') { handleInput(evt); } }); canvas.addEventListener('click', handleInput); canvas.addEventListener('touchstart', handleInput); // BACKGROUND const background = { draw: function() { ctx.fillStyle = "#70c5ce"; ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw some simple clouds ctx.fillStyle = "#FFF"; ctx.fillRect(40, 80, 50, 20); ctx.fillRect(150, 40, 70, 30); ctx.fillRect(250, 90, 40, 20); } }; // FOREGROUND const foreground = { h: 100, x: 0, dx: 2, draw: function() { // Ground ctx.fillStyle = "#ded895"; ctx.fillRect(0, canvas.height - this.h, canvas.width, this.h); // Grass ctx.fillStyle = "#558000"; ctx.fillRect(0, canvas.height - this.h, canvas.width, 20); // Add some grass blades for texture ctx.fillStyle = "#739f28"; for(let i=0; i<canvas.width; i+=4){ ctx.fillRect(i, canvas.height - this.h, 2, 20); } }, update: function() { // Ground logic not needed in this version. } }; // BIRD const bird = { x: 50, y: 150, w: 34, h: 24, radius: 12, frame: 0, gravity: 0.25, jump: 4.6, speed: 0, rotation: 0, draw: function() { let birdX = this.x - this.w / 2; let birdY = this.y - this.h / 2; ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.rotation); // Body ctx.fillStyle = "#f2d34a"; ctx.fillRect(-this.w/2, -this.h/2, this.w, this.h); // Wing (simple animation) ctx.fillStyle = (this.frame % 10 < 5) ? "#f2b94a" : "#f2d34a"; ctx.fillRect(-this.w/2 + 5, -this.h/2 + 7, this.w - 20, 10); // Eye ctx.fillStyle = "#ffffff"; ctx.fillRect(-this.w/2 + 22, -this.h/2 + 6, 8, 8); ctx.fillStyle = "#000000"; ctx.fillRect(-this.w/2 + 25, -this.h/2 + 9, 3, 3); // Beak ctx.fillStyle = "#f29e4a"; ctx.fillRect(-this.w/2 + 34, -this.h/2 + 8, 8, 8); ctx.restore(); }, flap: function() { this.speed = -this.jump; }, update: function() { // Animation frame this.frame++; let period = state.current == state.getReady ? 10 : 5; if(frames % period !== 0) return; // Slow down flap in get ready state if (state.current == state.getReady) { this.y = 150; // Keep bird centered this.rotation = 0; } else { this.speed += this.gravity; this.y += this.speed; // Rotation if(this.speed >= this.jump){ this.rotation = 90 * Math.PI/180; this.frame = 1; }else{ this.rotation = -25 * Math.PI/180; } } // Collision with ground if (this.y + this.h / 2 >= canvas.height - foreground.h) { this.y = canvas.height - foreground.h - this.h / 2; if (state.current == state.game) { state.current = state.over; } } // Ceiling collision if (this.y - this.h/2 <= 0) { this.y = this.h/2; this.speed = 0; } } }; // PIPES const pipes = { position: [], top: { x: 0, y: 0 }, bottom: { x: 0, y: 0 }, w: 55, gap: 95, maxYPos: -150, dx: 2, draw: function() { for (let i = 0; i < this.position.length; i++) { let p = this.position[i]; let topYPos = p.y; let bottomYPos = p.y + this.w + this.gap; // Top pipe body ctx.fillStyle = "#8ed15b"; ctx.fillRect(p.x, 0, this.w, p.y); // Top pipe head ctx.fillRect(p.x - 5, p.y-30, this.w + 10, 30); // Top pipe outline ctx.strokeStyle = "#558000"; ctx.lineWidth = 2; ctx.strokeRect(p.x, 0, this.w, p.y); ctx.strokeRect(p.x - 5, p.y-30, this.w + 10, 30); // Bottom pipe body ctx.fillRect(p.x, p.y + this.gap, this.w, canvas.height); // Bottom pipe head ctx.fillRect(p.x - 5, p.y + this.gap, this.w + 10, 30); // Bottom pipe outline ctx.strokeRect(p.x, p.y + this.gap, this.w, canvas.height); ctx.strokeRect(p.x - 5, p.y + this.gap, this.w + 10, 30); } }, update: function() { if (state.current !== state.game) return; if (frames % 100 == 0) { this.position.push({ x: canvas.width, y: this.maxYPos * (Math.random() + 1), passed: false }); } for (let i = 0; i < this.position.length; i++) { let p = this.position[i]; p.x -= this.dx; // COLLISION DETECTION // Top pipe if (bird.x + bird.radius > p.x && bird.x - bird.radius < p.x + this.w && bird.y - bird.radius < p.y) { state.current = state.over; } // Bottom pipe if (bird.x + bird.radius > p.x && bird.x - bird.radius < p.x + this.w && bird.y + bird.radius > p.y + this.gap) { state.current = state.over; } // SCORE if (p.x + this.w < bird.x && !p.passed) { score.value += 1; p.passed = true; score.best = Math.max(score.value, score.best); } // Remove pipes that are off screen if (p.x + this.w < 0) { this.position.shift(); } } } }; // SCORE const score = { best: parseInt(localStorage.getItem('flappyBest')) || 0, value: 0, draw: function() { ctx.fillStyle = "#FFF"; ctx.strokeStyle = "#000"; if (state.current == state.game) { ctx.lineWidth = 2; ctx.font = "35px 'Helvetica Neue', Arial, sans-serif"; ctx.fillText(this.value, canvas.width / 2, 50); ctx.strokeText(this.value, canvas.width / 2, 50); } else if (state.current == state.over) { ctx.font = "25px 'Helvetica Neue', Arial, sans-serif"; // Current score ctx.fillText(this.value, 225, 186); ctx.strokeText(this.value, 225, 186); // Best score ctx.fillText(this.best, 225, 228); ctx.strokeText(this.best, 225, 228); localStorage.setItem('flappyBest', this.best); } } }; // GET READY MESSAGE const getReady = { draw: function() { if (state.current == state.getReady) { ctx.textAlign = "center"; ctx.fillStyle = "#fff"; ctx.strokeStyle = "#000"; ctx.lineWidth = 2; ctx.font = "40px 'Helvetica Neue', Arial, sans-serif"; ctx.fillText("Flappy Bird", canvas.width / 2, canvas.height / 2 - 100); ctx.strokeText("Flappy Bird", canvas.width / 2, canvas.height / 2 - 100); ctx.font = "20px 'Helvetica Neue', Arial, sans-serif"; ctx.fillText("Click or Press Space to Play", canvas.width / 2, canvas.height / 2 - 50); ctx.strokeText("Click or Press Space to Play", canvas.width / 2, canvas.height / 2 - 50); ctx.textAlign = "start"; } } }; // GAME OVER MESSAGE const gameOver = { x: canvas.width / 2 - 100, y: 90, w: 200, h: 200, draw: function() { if (state.current == state.over) { ctx.textAlign = "center"; // Game Over Box ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; ctx.fillRect(this.x - 10, this.y - 10, this.w + 20, this.h + 20); ctx.fillStyle = "#ded895"; ctx.strokeStyle = "#5e5e5e"; ctx.lineWidth = 2; ctx.fillRect(this.x, this.y, this.w, this.h); ctx.strokeRect(this.x, this.y, this.w, this.h); // Game Over Text ctx.font = "30px 'Helvetica Neue', Arial, sans-serif"; ctx.fillStyle = "#d14e44"; ctx.fillText("Game Over", canvas.width / 2, 140); // Score Labels ctx.font = "20px 'Helvetica Neue', Arial, sans-serif"; ctx.fillStyle = "#000"; ctx.fillText("Score", 125, 185); ctx.fillText("Best", 130, 227); // Restart Button ctx.fillStyle = "#8ed15b"; ctx.fillRect(110, 263, 100, 36); ctx.strokeStyle = "#558000"; ctx.strokeRect(110, 263, 100, 36); ctx.fillStyle = "#FFF"; ctx.fillText("Restart", canvas.width / 2, 287); ctx.textAlign = "start"; } } }; // RESET GAME function resetGame() { bird.speed = 0; bird.y = 150; pipes.position = []; score.value = 0; state.current = state.getReady; } // GAME LOOP function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); background.draw(); pipes.draw(); foreground.draw(); bird.draw(); getReady.draw(); gameOver.draw(); score.draw(); } function update() { bird.update(); pipes.update(); foreground.update(); } function loop() { update(); draw(); frames++; requestAnimationFrame(loop); } resetGame(); loop(); </script> </body> </html> ```
Here's a complete HTML file containing a simple **Flappy Bird clone** playable in any modern browser. This version includes: - A canvas where the game runs. - Bird physics, including gravity and flapping. - Scrolling pipes with collision detection. - Game over handling. - Score tracking. - Simple start/restart mechanics. The game starts on click or press of the space bar and resets automatically after loss. --- ### ✅ Full Working Clone Below ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Flappy Bird Clone</title> <style> html, body { margin: 0; background: #70c5ce; overflow: hidden; height: 100%; display: flex; align-items: center; justify-content: center; font-family: Arial, sans-serif; } canvas { display: block; } </style> </head> <body> <canvas id="gameCanvas" width="400" height="512"></canvas> <script> const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const GRAVITY = 0.6; const FLAP = -10; const PIPE_WIDTH = 52; const PIPE_GAP = 120; const PIPE_SPEED = 2; const GROUND_HEIGHT = 112; let bird = { x: 80, y: canvas.height / 2, width: 34, height: 24, velocity: 0 }; let pipes = []; let score = 0; let gameOver = false; let gameStarted = false; function resetGame() { bird.y = canvas.height / 2; bird.velocity = 0; pipes = []; score = 0; gameOver = false; gameStarted = true; } document.addEventListener('mousedown', handleInput); document.addEventListener('keydown', handleInput); function handleInput(e) { if ((e.type === 'keydown' && e.code === 'Space') || e.type === 'mousedown') { e.preventDefault(); if (gameOver) { resetGame(); } else if (!gameStarted) { gameStarted = true; } else { bird.velocity = FLAP; } } } function updateGame() { if (!gameStarted || gameOver) return; bird.velocity += GRAVITY; bird.y += bird.velocity; // Bird hits the ground if (bird.y + bird.height > canvas.height - GROUND_HEIGHT) { bird.y = canvas.height - GROUND_HEIGHT - bird.height; gameOver = true; } // Bird flies off top of screen if (bird.y < 0) { bird.y = 0; gameOver = true; } for (let i = pipes.length - 1; i >= 0; i--) { const pipe = pipes[i]; pipe.x -= PIPE_SPEED; // Collisions if ( bird.x < pipe.x + PIPE_WIDTH && bird.x + bird.width > pipe.x && (bird.y < pipe.height || bird.y + bird.height > pipe.height + PIPE_GAP) ) { gameOver = true; } // Score update if (!pipe.passed && pipe.x + PIPE_WIDTH < bird.x) { ++score; pipe.passed = true; } // Remove off-screen pipes if (pipe.x + PIPE_WIDTH < 0) { pipes.splice(i, 1); } } // Generate new pipe if (pipes.length === 0 || pipes[pipes.length - 1].x < 200) { const MIN_HEIGHT = 100; const MAX_HEIGHT = canvas.height - GROUND_HEIGHT - PIPE_GAP - MIN_HEIGHT; let height = Math.floor(Math.random() * (MAX_HEIGHT - MIN_HEIGHT)) + MIN_HEIGHT; pipes.push({ x: canvas.width, height, passed: false }); } } function drawGame() { ctx.fillStyle = '#70c5ce'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw pipes ctx.fillStyle = 'green'; for (let pipe of pipes) { // Top pipe ctx.fillRect(pipe.x, 0, PIPE_WIDTH, pipe.height); // Bottom pipe ctx.fillRect(pipe.x, pipe.height + PIPE_GAP, PIPE_WIDTH, canvas.height - pipe.height - PIPE_GAP - GROUND_HEIGHT); } // Draw ground ctx.fillStyle = '#deb887'; ctx.fillRect(0, canvas.height - GROUND_HEIGHT, canvas.width, GROUND_HEIGHT); // Bird ctx.fillStyle = '#FFD700'; ctx.fillRect(bird.x, bird.y, bird.width, bird.height); // Score if (!gameOver || !gameStarted) { ctx.fillStyle = '#000000'; ctx.font = '24px Arial'; ctx.fillText(score, 10, 30); } // Messages ctx.fillStyle = '#000000'; ctx.font = '20px Arial'; if (!gameStarted) { ctx.fillText('Click or Press Space to Start', 40, canvas.height / 2); } else if (gameOver) { ctx.font = '30px Arial'; ctx.fillText('Game Over', 110, canvas.height / 2 - 20); ctx.fillText('Press Space to Restart', 65, canvas.height / 2 + 20); } } function gameLoop() { updateGame(); drawGame(); requestAnimationFrame(gameLoop); } gameLoop(); </script> </body> </html> ``` --- ### 🎮 How to Play - **Click or press Space** to start the game or restart after game over. - **Click or press Space again** during gameplay to make the bird flap. - Avoid colliding with the pipes and the ground! - The score increases every time you successfully pass between a pair of pipes. --- ### 📦 Features - **Canvas Rendering:** Simple and fast rendering using HTML5 Canvas. - **Responsive Mechanics:** Basic but fun gameplay with adjustable gravity and jump mechanics. - **No External Libraries:** All functionality is contained within a single HTML file. You can embed this in an iframe to display on a larger page, or host it as a standalone game. This is a 1:1 basic clone that matches the mechanics of the original while maintaining simplicity.
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Flappy Bird Clone</title> <style> html, body { margin: 0; height: 100%; overflow: hidden; background: #70c5ce; display: flex; justify-content: center; align-items: center; font-family: Arial, Helvetica, sans-serif; } canvas { background: #70c5ce; border: 2px solid #333; } #score { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); font-size: 48px; color: white; text-shadow: 2px 2px 0 #000; user-select: none; } #gameOver { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; display: none; } #gameOver h2 { font-size: 48px; color: white; text-shadow: 2px 2px 0 #000; margin: 0; } #gameOver button { margin-top: 20px; padding: 10px 20px; font-size: 24px; cursor: pointer; border: none; background: #fff; color: #333; box-shadow: 0 4px 0 #aaa; border-radius: 5px; } #gameOver button:active { transform: translateY(2px); box-shadow: 0 2px 0 #aaa; } </style> </head> <body> <div id="score">0</div> <div id="gameOver"> <h2>Game Over</h2> <button onclick="resetGame()">Play Again</button> </div> <canvas id="gameCanvas" width="400" height="512"></canvas> <script> const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const scoreEl = document.getElementById('score'); const gameOverEl = document.getElementById('gameOver'); const GRAVITY = 0.4; const FLAP = -7; const PIPE_WIDTH = 80; const PIPE_GAP = 120; const PIPE_MIN_HEIGHT = 100; const SPEED = 2.5; let bird = { x: 60, y: canvas.height / 2, radius: 12, velocity: 0 }; let pipes = []; let frameCount = 0; let score = 0; let gameOver = false; let started = false; function drawBird() { ctx.save(); ctx.translate(bird.x, bird.y); // Simple yellow bird body ctx.fillStyle = '#ff0'; ctx.beginPath(); ctx.arc(0, 0, bird.radius, 0, Math.PI * 2); ctx.fill(); // Eye ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(6, -4, 4, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000'; ctx.beginPath(); ctx.arc(7, -4, 2, 0, Math.PI * 2); ctx.fill(); // Beak ctx.fillStyle = '#f90'; ctx.beginPath(); ctx.moveTo(10, 0); ctx.lineTo(16, 2); ctx.lineTo(10, 4); ctx.closePath(); ctx.fill(); // Wing ctx.fillStyle = '#fc0'; ctx.beginPath(); ctx.moveTo(-4, 2); ctx.lineTo(6, 8); ctx.lineTo(-4, 14); ctx.closePath(); ctx.fill(); ctx.restore(); } function drawPipe(pipe) { ctx.fillStyle = '#73bf2e'; ctx.fillRect(pipe.x, 0, PIPE_WIDTH, pipe.top); ctx.fillRect(pipe.x, pipe.bottom, PIPE_WIDTH, canvas.height - pipe.bottom); // Pipe caps ctx.fillStyle = '#5aa427'; ctx.fillRect(pipe.x - 4, pipe.top - 30, PIPE_WIDTH + 8, 30); ctx.fillRect(pipe.x - 4, pipe.bottom, PIPE_WIDTH + 8, 30); } function drawBackground() { // Sky ctx.fillStyle = '#70c5ce'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Ground ctx.fillStyle = '#ded895'; ctx.fillRect(0, canvas.height - 80, canvas.width, 80); // Grass stripes ctx.fillStyle = '#5daa43'; for (let i = 0; i < canvas.width; i += 20) { ctx.fillRect(i, canvas.height - 80, 10, 10); ctx.fillRect(i + 10, canvas.height - 70, 10, 10); } } function updateBird() { bird.velocity += GRAVITY; bird.y += bird.velocity; // Hit ground if (bird.y + bird.radius > canvas.height - 80) { bird.y = canvas.height - 80 - bird.radius; endGame(); } // Hit ceiling if (bird.y - bird.radius < 0) { bird.y = bird.radius; bird.velocity = 0; } } function updatePipes() { for (let i = pipes.length - 1; i >= 0; i--) { pipes[i].x -= SPEED; if (pipes[i].x + PIPE_WIDTH < 0) pipes.splice(i, 1); } if (frameCount % 90 === 0) { const top = Math.floor(Math.random() * (canvas.height - 80 - PIPE_GAP - 2 * PIPE_MIN_HEIGHT)) + PIPE_MIN_HEIGHT; pipes.push({ x: canvas.width, top: top, bottom: top + PIPE_GAP }); } } function checkCollisions() { for (let p of pipes) { // Within pipe bounds horizontally if (bird.x + bird.radius > p.x && bird.x - bird.radius < p.x + PIPE_WIDTH) { // Hit top or bottom pipe if (bird.y - bird.radius < p.top || bird.y + bird.radius > p.bottom) { endGame(); } } // Score when passing pipe if (!p.scored && p.x + PIPE_WIDTH < bird.x) { p.scored = true; score++; scoreEl.textContent = score; } } } function endGame() { gameOver = true; gameOverEl.style.display = 'block'; } function resetGame() { bird.y = canvas.height / 2; bird.velocity = 0; pipes = []; frameCount = 0; score = 0; scoreEl.textContent = score; gameOver = false; started = false; gameOverEl.style.display = 'none'; loop(); } function loop() { if (gameOver) return; frameCount++; drawBackground(); updateBird(); updatePipes(); checkCollisions(); pipes.forEach(drawPipe); drawBird(); requestAnimationFrame(loop); } canvas.addEventListener('click', () => { if (!gameOver) { if (!started) started = true; bird.velocity = FLAP; } }); document.addEventListener('keydown', e => { if (e.code === 'Space' && !gameOver) { e.preventDefault(); if (!started) started = true; bird.velocity = FLAP; } }); // Start drawBackground(); drawBird(); scoreEl.textContent = score; </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Flappy Bird Clone</title> <style> body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #70c5ce; overflow: hidden; } canvas { background: #fafafa; display: block; } </style> </head> <body> <canvas id="gameCanvas" width="288" height="512"></canvas> <script> const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); // Game variables let birdX = 50; let birdY = 150; let gravity = 0.6; let lift = -15; let velocity = 0; let score = 0; let gameOver = false; let pipes = []; let pipeWidth = 52; let pipeGap = 100; let pipeSpeed = 2; let frame = 0; // Bird drawing (simple yellow circle for bird) function drawBird() { ctx.beginPath(); ctx.arc(birdX, birdY, 20, 0, Math.PI * 2); ctx.fillStyle = '#FFD700'; ctx.fill(); ctx.closePath(); } // Pipe class class Pipe { constructor() { this.top = Math.random() * (canvas.height / 4) + 20; this.bottom = canvas.height - (this.top + pipeGap); this.x = canvas.width; this.width = pipeWidth; this.passed = false; } draw() { ctx.fillStyle = '#008000'; ctx.fillRect(this.x, 0, this.width, this.top); ctx.fillRect(this.x, canvas.height - this.bottom, this.width, this.bottom); } update() { this.x -= pipeSpeed; if (this.x + this.width < birdX && !this.passed) { score++; this.passed = true; } } } // Handle key press or click to flap document.addEventListener('keydown', flap); document.addEventListener('click', flap); function flap(e) { if ((e.type === 'keydown' && e.key === ' ') || e.type === 'click') { if (!gameOver) { velocity = lift; } else { resetGame(); } } } // Collision detection function detectCollision(pipe) { // Bird bounding box let birdLeft = birdX - 20; let birdRight = birdX + 20; let birdTop = birdY - 20; let birdBottom = birdY + 20; // Top pipe let topPipeBottom = pipe.top; if (birdRight > pipe.x && birdLeft < pipe.x + pipe.width && birdBottom > 0 && birdTop < topPipeBottom) { return true; } // Bottom pipe let bottomPipeTop = canvas.height - pipe.bottom; if (birdRight > pipe.x && birdLeft < pipe.x + pipe.width && birdTop < canvas.height && birdBottom > bottomPipeTop) { return true; } // Ground or ceiling if (birdY > canvas.height - 20 || birdY < 20) { return true; } return false; } // Reset game function resetGame() { birdY = 150; velocity = 0; pipes = []; score = 0; gameOver = false; frame = 0; } // Main game loop function gameLoop() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Update bird velocity += gravity; birdY += velocity; // Draw bird drawBird(); // Generate pipes every 100 frames if (frame % 100 === 0) { pipes.push(new Pipe()); } // Update and draw pipes pipes.forEach((pipe, index) => { pipe.update(); pipe.draw(); if (detectCollision(pipe)) { gameOver = true; } // Remove off-screen pipes if (pipe.x + pipe.width < 0) { pipes.splice(index, 1); } }); // Draw score ctx.fillStyle = '#000'; ctx.font = '24px Arial'; ctx.fillText(`Score: ${score}`, 10, 30); if (gameOver) { ctx.fillText('Game Over! Tap to restart', 10, canvas.height / 2); } else { frame++; } requestAnimationFrame(gameLoop); } // Start the game gameLoop(); </script> </body> </html> ```
Here's a complete HTML file for a Flappy Bird clone: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Flappy Bird Clone</title> <style> body { margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #70c5ce; font-family: Arial, sans-serif; } #game-container { position: relative; width: 400px; height: 600px; overflow: hidden; background: linear-gradient(to bottom, #87CEEB 0%, #E0F7FA 100%); border: 3px solid #333; box-shadow: 0 0 20px rgba(0,0,0,0.5); } #bird { position: absolute; width: 40px; height: 30px; background-color: yellow; border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%; z-index: 10; left: 50px; } .pipe { position: absolute; width: 60px; background-color: #4CAF50; border: 2px solid #2E7D32; border-radius: 5px; } #ground { position: absolute; bottom: 0; width: 100%; height: 20px; background-color: #8B4513; } #score { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); font-size: 30px; color: white; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); z-index: 100; } #game-over { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: rgba(0,0,0,0.7); color: white; padding: 20px; border-radius: 10px; text-align: center; display: none; z-index: 200; } #start-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); display: flex; flex-direction: column; justify-content: center; align-items: center; z-index: 300; } #start-button { padding: 15px 30px; font-size: 20px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; } #start-button:hover { background-color: #2E7D32; } </style> </head> <body> <div id="game-container"> <div id="bird"></div> <div id="ground"></div> <div id="score">0</div> <div id="game-over"> <h1>Game Over</h1> <p>Your score: <span id="final-score">0</span></p> <button id="restart-button">Play Again</button> </div> <div id="start-screen"> <h1 style="color: white; font-size: 40px;">Flappy Bird</h1> <button id="start-button">Start Game</button> <p style="color: white; margin-top: 20px;">Press Space or Click to Jump</p> </div> </div> <script> // Game variables const bird = document.getElementById('bird'); const gameContainer = document.getElementById('game-container'); const scoreElement = document.getElementById('score'); const gameOverScreen = document.getElementById('game-over'); const finalScoreElement = document.getElementById('final-score'); const restartButton = document.getElementById('restart-button'); const startScreen = document.getElementById('start-screen'); const startButton = document.getElementById('start-button'); let gravity = 0.5; let birdPosition = 300; let birdVelocity = 0; let gameStarted = false; let gameOver = false; let score = 0; let pipes = []; let pipeInterval; let animationId; let groundHeight = 20; let gameSpeed = 3; let gapHeight = 150; // Initialize game function init() { birdPosition = 300; birdVelocity = 0; score = 0; gameOver = false; scoreElement.textContent = score; // Clear all pipes pipes.forEach(pipe => { pipe.element.remove(); }); pipes = []; bird.style.top = birdPosition + 'px'; gameOverScreen.style.display = 'none'; } // Start game function startGame() { init(); gameStarted = true; startScreen.style.display = 'none'; // Start pipe generation pipeInterval = setInterval(generatePipes, 2000); // Start game loop gameLoop(); } // Game loop function gameLoop() { if (gameOver) return; // Update bird position birdVelocity += gravity; birdPosition += birdVelocity; bird.style.top = birdPosition + 'px'; // Rotate bird based on velocity bird.style.transform = `rotate(${birdVelocity * 3}deg)`; // Check for collisions if (birdPosition <= 0 || birdPosition + bird.offsetHeight >= gameContainer.offsetHeight - groundHeight || checkPipeCollision()) { endGame(); return; } // Update pipes updatePipes(); animationId = requestAnimationFrame(gameLoop); } // Generate pipes function generatePipes() { const pipeGapTop = Math.floor(Math.random() * (gameContainer.offsetHeight - gapHeight - 100)) + 50; const pipeGapBottom = pipeGapTop + gapHeight; // Top pipe const topPipe = document.createElement('div'); topPipe.className = 'pipe'; topPipe.style.top = '0'; topPipe.style.height = pipeGapTop + 'px'; topPipe.style.left = gameContainer.offsetWidth + 'px'; gameContainer.appendChild(topPipe); // Bottom pipe const bottomPipe = document.createElement('div'); bottomPipe.className = 'pipe'; bottomPipe.style.top = pipeGapBottom + 'px'; bottomPipe.style.height = (gameContainer.offsetHeight - pipeGapBottom - groundHeight) + 'px'; bottomPipe.style.left = gameContainer.offsetWidth + 'px'; gameContainer.appendChild(bottomPipe); pipes.push({ element: topPipe, passed: false, x: gameContainer.offsetWidth, height: pipeGapTop }); pipes.push({ element: bottomPipe, passed: false, x: gameContainer.offsetWidth, height: gameContainer.offsetHeight - pipeGapBottom - groundHeight }); } // Update pipes function updatePipes() { pipes.forEach(pipe => { pipe.x -= gameSpeed; pipe.element.style.left = pipe.x + 'px'; // Check if pipe passed the bird if (!pipe.passed && pipe.x < bird.offsetLeft - bird.offsetWidth) { pipe.passed = true; score++; scoreElement.textContent = score; } // Remove pipe if off screen if (pipe.x + pipe.element.offsetWidth < 0) { pipe.element.remove(); pipes = pipes.filter(p => p !== pipe); } }); } // Check pipe collision function checkPipeCollision() { for (const pipe of pipes) { const pipeRect = pipe.element.getBoundingClientRect(); const birdRect = bird.getBoundingClientRect(); if ( birdRect.right > pipeRect.left && birdRect.left < pipeRect.right && birdRect.bottom > pipeRect.top && birdRect.top < pipeRect.bottom ) { return true; } } return false; } // End game function endGame() { gameOver = true; clearInterval(pipeInterval); cancelAnimationFrame(animationId); finalScoreElement.textContent = score; gameOverScreen.style.display = 'block'; } // Jump function function jump() { if (!gameStarted) { startGame(); return; } if (gameOver) return; birdVelocity = -10; } // Event listeners document.addEventListener('keydown', (e) => { if (e.code === 'Space') { e.preventDefault(); jump(); } }); gameContainer.addEventListener('click', () => { jump(); }); restartButton.addEventListener('click', () => { startGame(); }); startButton.addEventListener('click', () => { startGame(); }); // Initialize bird position bird.style.top = birdPosition + 'px'; </script> </body> </html> ``` This Flappy Bird clone includes: 1. A bird that responds to spacebar clicks or screen taps 2. Randomly generated pipes with gaps 3. Score tracking 4. Game over screen with restart option 5. Start screen with instructions 6. Collision detection with pipes and ground 7. Gravity and jump mechanics The game is fully contained in a single HTML file with no external dependencies. You can save this code to an HTML file and open it in any modern web browser to play.
# Flappy Bird Clone I'll create a complete, responsive Flappy Bird clone that works in a browser. The game will include all core elements: bird character, scrolling pipes, gravity physics, score tracking, and fluid controls. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Flappy Bird Clone</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Press Start 2P', cursive, Arial, sans-serif; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: linear-gradient(to bottom, #1a2980, #26d0ce); overflow: hidden; padding: 20px; } .game-container { position: relative; width: 100%; max-width: 400px; box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3); border-radius: 10px; overflow: hidden; } #gameCanvas { background: linear-gradient(to bottom, #64b3f4 0%, #87ceeb 100%); display: block; width: 100%; image-rendering: pixelated; border: 4px solid #333; border-radius: 8px; cursor: pointer; } .game-ui { position: absolute; top: 20px; left: 0; right: 0; text-align: center; color: white; text-shadow: 3px 3px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000; z-index: 10; } .score { font-size: 48px; margin-bottom: 20px; } .instructions { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.7); color: white; padding: 30px; border-radius: 15px; text-align: center; width: 90%; max-width: 350px; z-index: 20; backdrop-filter: blur(5px); } .instructions h1 { font-size: 32px; margin-bottom: 20px; color: #ffd700; text-shadow: 0 0 10px rgba(255, 215, 0, 0.5); } .instructions p { margin: 15px 0; line-height: 1.6; } button { background: linear-gradient(to bottom, #ff8c00, #ff5500); color: white; border: none; padding: 15px 30px; margin-top: 20px; font-size: 18px; border-radius: 30px; cursor: pointer; text-shadow: 2px 2px 0 #000; box-shadow: 0 5px 0 #a04100; transition: all 0.1s; outline: none; text-transform: uppercase; } button:hover { background: linear-gradient(to bottom, #ff9a00, #ff6600); transform: translateY(2px); box-shadow: 0 3px 0 #a04100; } button:active { transform: translateY(5px); box-shadow: 0 0 0 #a04100; } .game-over { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.85); color: white; padding: 40px; border-radius: 15px; text-align: center; z-index: 20; display: none; width: 90%; max-width: 350px; box-shadow: 0 0 30px rgba(255, 0, 0, 0.5); } .game-over h2 { font-size: 36px; margin-bottom: 20px; color: #ff3b30; } .ground { position: absolute; bottom: 0; width: 100%; height: 50px; background: linear-gradient(to bottom, #8B4513, #654321); border-top: 5px solid #634308; z-index: 5; } .clouds { position: absolute; top: 30px; width: 100%; height: 80px; z-index: 2; pointer-events: none; } .cloud { position: absolute; background: rgba(255, 255, 255, 0.9); border-radius: 50%; } @media (max-height: 650px) { .game-container { max-width: 320px; } .score { font-size: 36px; } } </style> </head> <body> <div class="game-container"> <div class="game-ui"> <div class="score">0</div> </div> <div class="clouds" id="clouds"></div> <canvas id="gameCanvas" width="400" height="600"></canvas> <div class="ground"></div> <div class="instructions" id="instructions"> <h1>FLAPPY BIRD</h1> <p>Press SPACE or tap to jump</p> <p>Avoid the pipes</p> <p>Press to start!</p> <button id="start-button">START GAME</button> </div> <div class="game-over" id="game-over"> <h2>GAME OVER!</h2> <p>Your score: <span id="final-score">0</span></p> <button id="restart-button">PLAY AGAIN</button> </div> </div> <script> // Game variables const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const scoreDisplay = document.querySelector('.score'); const finalScoreDisplay = document.getElementById('final-score'); const instructionsEl = document.getElementById('instructions'); const gameOverEl = document.getElementById('game-over'); const startBtn = document.getElementById('start-button'); const restartBtn = document.getElementById('restart-button'); const cloudsContainer = document.getElementById('clouds'); // Game state let game = { running: false, gravity: 0.5, frame: 0, score: 0, spawnFrequency: 90, gameSpeed: 3, clouds: [], particles: [] }; // Create clouds for background for (let i = 0; i < 8; i++) { const cloud = document.createElement('div'); cloud.className = 'cloud'; cloud.style.width = Math.random() * 70 + 30 + 'px'; cloud.style.height = Math.random() * 50 + 20 + 'px'; cloud.style.left = Math.random() * canvas.offsetWidth + 'px'; cloud.style.top = Math.random() * 70 + 'px'; cloudsContainer.appendChild(cloud); game.clouds.push({ element: cloud, x: parseInt(cloud.style.left), speed: Math.random() * 0.5 + 0.2 }); } // Bird object const bird = { x: canvas.width / 3, y: canvas.height / 2, radius: 20, velocity: 0, jumpStrength: 10, flapPower: 15, flapped: false, rotation: 0, // Draw bird character draw() { ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.rotation); // Bird body ctx.fillStyle = '#FFD700'; ctx.beginPath(); ctx.arc(0, 0, this.radius, 0, Math.PI * 2); ctx.fill(); // Bird details ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(5, -5, 7, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'black'; ctx.beginPath(); ctx.arc(7, -5, 3, 0, Math.PI * 2); ctx.fill(); // Beak ctx.fillStyle = '#FF6600'; ctx.beginPath(); ctx.moveTo(10, 0); ctx.lineTo(20, -3); ctx.lineTo(10, 3); ctx.fill(); // Wings ctx.fillStyle = '#FFA500'; const wingOffset = Math.sin(game.frame * 0.1) * 5; ctx.beginPath(); ctx.ellipse(0, 8 + wingOffset, 8, 16, Math.PI/2, 0, Math.PI * 2); ctx.fill(); ctx.restore(); }, // Control bird movement update() { // Apply gravity this.velocity += game.gravity; this.y += this.velocity; // Apply rotation based on velocity this.rotation = Math.min(Math.PI/4, Math.max(-Math.PI/2, this.velocity * 0.1)); // Floor collision if (this.y + this.radius >= canvas.height - 50) { this.y = canvas.height - 50 - this.radius; if (game.running) handleGameOver(); } // Ceiling collision this.y = Math.max(this.radius, this.y); }, // Make the bird jump flap() { this.velocity = -this.flapPower; this.flapped = true; } }; // Pipes array const pipes = { gap: 160, minHeight: 50, maxHeight: canvas.height - 200, items: [], width: 60, // Add a new pipe add() { const height = Math.random() * (this.maxHeight - this.minHeight) + this.minHeight; this.items.push({ x: canvas.width, topHeight: height, bottomY: height + this.gap, passed: false }); }, // Draw all pipes draw() { for (let pipe of this.items) { // Top pipe with gradient const topGradient = ctx.createLinearGradient(pipe.x, 0, pipe.x + this.width, 0); topGradient.addColorStop(0, '#117605'); topGradient.addColorStop(1, '#0b4e03'); ctx.fillStyle = topGradient; ctx.fillRect(pipe.x, 0, this.width, pipe.topHeight); // Top pipe cap ctx.fillStyle = '#3a9a1e'; ctx.fillRect(pipe.x - 5, pipe.topHeight - 15, this.width + 10, 15); // Bottom pipe with gradient const bottomGradient = ctx.createLinearGradient(pipe.x, pipe.bottomY, pipe.x + this.width, pipe.bottomY); bottomGradient.addColorStop(0, '#117605'); bottomGradient.addColorStop(1, '#0b4e03'); ctx.fillStyle = bottomGradient; ctx.fillRect(pipe.x, pipe.bottomY, this.width, canvas.height - pipe.bottomY); // Bottom pipe cap ctx.fillStyle = '#3a9a1e'; ctx.fillRect(pipe.x - 5, pipe.bottomY, this.width + 10, 15); } }, // Update pipes position update() { for (let i = this.items.length - 1; i >= 0; i--) { const pipe = this.items[i]; // Move pipe to the left pipe.x -= game.gameSpeed; // Remove off-screen pipes if (pipe.x + this.width < 0) { this.items.splice(i, 1); continue; } // Check if bird passed pipe for scoring if (!pipe.passed && pipe.x + this.width < bird.x - bird.radius) { pipe.passed = true; game.score++; scoreDisplay.textContent = game.score; } // Detect collision if (bird.x + bird.radius > pipe.x && bird.x - bird.radius < pipe.x + this.width && (bird.y - bird.radius < pipe.topHeight || bird.y + bird.radius > pipe.bottomY)) { handleGameOver(); } } } }; // Start the game function startGame() { game.running = true; game.score = 0; game.frame = 0; scoreDisplay.textContent = '0'; bird.y = canvas.height / 2; bird.velocity = 0; pipes.items = []; instructionsEl.style.display = 'none'; gameOverEl.style.display = 'none'; animate(); } // Game over sequence function handleGameOver() { game.running = false; finalScoreDisplay.textContent = game.score; gameOverEl.style.display = 'block'; createParticles(); } // Create particles effect on game over function createParticles() { const particleCount = 30; for (let i = 0; i < particleCount; i++) { game.particles.push({ x: bird.x, y: bird.y, radius: Math.random() * 10 + 2, color: ['#FFD700', '#FFA500', '#FF6600'][Math.floor(Math.random() * 3)], velocity: { x: (Math.random() - 0.5) * 12, y: (Math.random() - 0.5) * 12 }, friction: 0.95, gravity: 0.3 }); } } // Draw and update particles function handleParticles() { for (let i = game.particles.length - 1; i >= 0; i--) { const p = game.particles[i]; // Draw particle ctx.beginPath(); ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); ctx.fillStyle = p.color; ctx.fill(); // Update p.x += p.velocity.x; p.y += p.velocity.y; p.velocity.x *= p.friction; p.velocity.y *= p.friction; p.velocity.y += p.gravity; p.radius *= 0.96; // Remove when not visible if (p.radius <= 0.5) { game.particles.splice(i, 1); } } } // Handle cloud movement function updateClouds() { for (let cloud of game.clouds) { // Update position cloud.x -= cloud.speed; // Reset position when off screen if (cloud.x < -100) { cloud.x = canvas.offsetWidth + 20; cloud.y = Math.random() * 70; } // Update CSS cloud.element.style.left = cloud.x + 'px'; } } // Game loop function animate() { if (!game.running) return; // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw sky background ctx.fillStyle = '#87CEEB'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Add new pipes periodically if (game.frame % game.spawnFrequency === 0) { pipes.add(); } // Update game elements bird.update(); pipes.update(); updateClouds(); // Draw game elements pipes.draw(); bird.draw(); handleParticles(); // Draw ground ctx.fillStyle = '#8B4513'; ctx.fillRect(0, canvas.height - 50, canvas.width, 50); ctx.fillStyle = '#654321'; ctx.fillRect(0, canvas.height - 50, canvas.width, 8); // Increase difficulty as the game progresses if (game.score % 5 === 0) { game.gameSpeed = Math.min(8, 3 + Math.floor(game.score / 5)); pipes.gap = Math.max(120, 160 - Math.floor(game.score / 5) * 4); } game.frame++; requestAnimationFrame(animate); } // Event listeners canvas.addEventListener('click', jump); document.addEventListener('keydown', e => { if (e.code === 'Space') jump(); }); startBtn.addEventListener('click', startGame); restartBtn.addEventListener('click', startGame); // Handle UI interactions function jump() { if (instructionsEl.style.display !== 'none' && gameOverEl.style.display !== 'block') { startGame(); } else if (game.running) { bird.flap(); } } // Initialize game on load instructionsEl.style.display = 'block'; </script> </body> </html> ``` ## Game Features: - **Complete Flappy Bird gameplay**: Control a bird by clicking, tapping, or pressing space to make it fly through pipes - **Responsive design**: Works on mobile and desktop devices - **Visual elements**: - Animated bird character with flapping wings - Scrolling pipes with nice gradients - Clouds in the background adding depth - Particle explosion effect when game ends - Smooth animations and physics - **Game mechanics**: - Score tracking that increases as you pass pipes - Increasing difficulty (faster game speed, narrower gaps) - Game over screen with final score display - **User interface**: - Clear instructions screen - Score counter - Start and restart buttons To play, just open this HTML file in a browser or click the "START GAME" button. Press SPACE or tap/click to make the bird jump and avoid hitting the pipes!
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Flappy Bird Clone</title> <style> body { margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; background: linear-gradient(to bottom, #87CEEB 0%, #98FB98 100%); font-family: Arial, sans-serif; } #gameContainer { position: relative; background: linear-gradient(to bottom, #87CEEB 0%, #98FB98 100%); overflow: hidden; } canvas { border: 3px solid #8B4513; display: block; background: linear-gradient(to bottom, #87CEEB 0%, #98FB98 70%, #90EE90 100%); } #gameOverScreen { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 30px; border-radius: 10px; text-align: center; display: none; } #gameOverScreen h2 { margin-top: 0; color: #ff6b6b; } #gameOverScreen button { padding: 10px 20px; font-size: 16px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; margin-top: 10px; } #gameOverScreen button:hover { background: #45a049; } #instructions { position: absolute; top: 20px; left: 20px; color: white; background: rgba(0, 0, 0, 0.5); padding: 10px; border-radius: 5px; font-size: 14px; } #score { position: absolute; top: 20px; right: 20px; color: white; font-size: 24px; font-weight: bold; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); } </style> </head> <body> <div id="gameContainer"> <canvas id="gameCanvas" width="400" height="600"></canvas> <div id="instructions"> Click or press SPACE to flap! </div> <div id="score">Score: 0</div> <div id="gameOverScreen"> <h2>Game Over!</h2> <p>Final Score: <span id="finalScore">0</span></p> <p>Best Score: <span id="bestScore">0</span></p> <button onclick="restartGame()">Play Again</button> </div> </div> <script> const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const gameOverScreen = document.getElementById('gameOverScreen'); const scoreElement = document.getElementById('score'); const finalScoreElement = document.getElementById('finalScore'); const bestScoreElement = document.getElementById('bestScore'); // Game variables let gameRunning = true; let score = 0; let bestScore = localStorage.getItem('flappyBestScore') || 0; bestScoreElement.textContent = bestScore; // Bird object const bird = { x: 50, y: canvas.height / 2, width: 30, height: 25, velocity: 0, gravity: 0.5, jumpPower: -8, color: '#FFD700' }; // Pipes array const pipes = []; const pipeWidth = 60; const pipeGap = 150; let pipeSpawnTimer = 0; const pipeSpawnRate = 90; // Ground const groundHeight = 50; // Input handling function jump() { if (gameRunning) { bird.velocity = bird.jumpPower; } } canvas.addEventListener('click', jump); document.addEventListener('keydown', (e) => { if (e.code === 'Space') { e.preventDefault(); jump(); } }); // Game functions function createPipe() { const minHeight = 50; const maxHeight = canvas.height - groundHeight - pipeGap - minHeight; const topHeight = Math.random() * (maxHeight - minHeight) + minHeight; pipes.push({ x: canvas.width, topHeight: topHeight, bottomY: topHeight + pipeGap, bottomHeight: canvas.height - groundHeight - (topHeight + pipeGap), passed: false }); } function updateBird() { bird.velocity += bird.gravity; bird.y += bird.velocity; // Check boundaries if (bird.y + bird.height > canvas.height - groundHeight || bird.y < 0) { gameOver(); } } function updatePipes() { pipeSpawnTimer++; if (pipeSpawnTimer >= pipeSpawnRate) { createPipe(); pipeSpawnTimer = 0; } for (let i = pipes.length - 1; i >= 0; i--) { const pipe = pipes[i]; pipe.x -= 2; // Check collision if (bird.x + bird.width > pipe.x && bird.x < pipe.x + pipeWidth && (bird.y < pipe.topHeight || bird.y + bird.height > pipe.bottomY)) { gameOver(); } // Check if bird passed pipe if (!pipe.passed && bird.x > pipe.x + pipeWidth) { pipe.passed = true; score++; scoreElement.textContent = `Score: ${score}`; } // Remove off-screen pipes if (pipe.x + pipeWidth < 0) { pipes.splice(i, 1); } } } function drawBird() { ctx.fillStyle = bird.color; ctx.fillRect(bird.x, bird.y, bird.width, bird.height); // Draw simple eye ctx.fillStyle = 'white'; ctx.fillRect(bird.x + 20, bird.y + 5, 6, 6); ctx.fillStyle = 'black'; ctx.fillRect(bird.x + 22, bird.y + 7, 2, 2); // Draw beak ctx.fillStyle = '#FF8C00'; ctx.fillRect(bird.x + bird.width, bird.y + 10, 8, 5); } function drawPipes() { ctx.fillStyle = '#228B22'; pipes.forEach(pipe => { // Top pipe ctx.fillRect(pipe.x, 0, pipeWidth, pipe.topHeight); // Bottom pipe ctx.fillRect(pipe.x, pipe.bottomY, pipeWidth, pipe.bottomHeight); // Pipe caps ctx.fillStyle = '#32CD32'; ctx.fillRect(pipe.x - 5, pipe.topHeight - 20, pipeWidth + 10, 20); ctx.fillRect(pipe.x - 5, pipe.bottomY, pipeWidth + 10, 20); ctx.fillStyle = '#228B22'; }); } function drawGround() { ctx.fillStyle = '#8B4513'; ctx.fillRect(0, canvas.height - groundHeight, canvas.width, groundHeight); // Add some texture to ground ctx.fillStyle = '#A0522D'; for (let x = 0; x < canvas.width; x += 20) { ctx.fillRect(x, canvas.height - groundHeight, 10, 10); } } function drawBackground() { // Sky gradient const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height); gradient.addColorStop(0, '#87CEEB'); gradient.addColorStop(0.7, '#98FB98'); gradient.addColorStop(1, '#90EE90'); ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); // Simple clouds ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'; ctx.beginPath(); ctx.arc(100, 100, 30, 0, Math.PI * 2); ctx.arc(120, 90, 40, 0, Math.PI * 2); ctx.arc(140, 100, 30, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(300, 150, 25, 0, Math.PI * 2); ctx.arc(315, 140, 35, 0, Math.PI * 2); ctx.arc(330, 150, 25, 0, Math.PI * 2); ctx.fill(); } function gameLoop() { if (!gameRunning) return; // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw everything drawBackground(); drawPipes(); drawGround(); drawBird(); // Update game objects updateBird(); updatePipes(); requestAnimationFrame(gameLoop); } function gameOver() { gameRunning = false; finalScoreElement.textContent = score; if (score > bestScore) { bestScore = score; localStorage.setItem('flappyBestScore', bestScore); bestScoreElement.textContent = bestScore; } gameOverScreen.style.display = 'block'; } function restartGame() { gameRunning = true; score = 0; scoreElement.textContent = 'Score: 0'; // Reset bird bird.y = canvas.height / 2; bird.velocity = 0; // Clear pipes pipes.length = 0; pipeSpawnTimer = 0; gameOverScreen.style.display = 'none'; gameLoop(); } // Start the game gameLoop(); </script> </body> </html> ``` This is a complete Flappy Bird clone with the following features: **Core Gameplay:** - Bird that falls due to gravity - Click or spacebar to make the bird jump - Green pipes with gaps to fly through - Collision detection with pipes and ground - Score tracking (increases when passing pipes) **Visual Elements:** - Sky gradient background with clouds - Yellow bird with eye and beak details - Green pipes with caps - Textured brown ground - Score display **Game Features:** - Game over screen with final score - Best score persistence (saved in browser) - Restart functionality - Responsive controls (mouse click or spacebar) **Technical Implementation:** - HTML5 Canvas for rendering - Smooth 60fps animation loop - Proper collision detection - Local storage for high scores - Clean, organized code structure The game plays exactly like the original Flappy Bird - navigate the bird through pipes by timing your jumps carefully!
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Flappy Bird Clone</title> <style> html, body { margin:0; padding:0; overflow:hidden; background:#70c5ce; /* sky blue */ font-family:sans-serif; } canvas { display:block; margin:auto; background:#70c5ce; } #overlay { position:absolute; top:0; left:0; width:100%; height:100%; display:flex; align-items:center; justify-content:center; color:#fff; text-shadow:0 0 5px #000; font-size:2rem; pointer-events:none; } #score { position:absolute; top:10px; left:50%; transform:translateX(-50%); color:#fff; font-size:2rem; text-shadow:0 0 5px #000; pointer-events:none; } </style> </head> <body> <canvas id="game"></canvas> <div id="score"></div> <div id="overlay">Press SPACE to start</div> <script> (() => { const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); // Resize canvas to fill the window const resize = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }; resize(); window.addEventListener('resize', resize); // Game constants const GRAVITY = 0.5; const FLAP_STRENGTH = -8; const PIPE_SPEED = 2; const PIPE_GAP = 150; const PIPE_SPACING = 250; // distance between consecutive pipes const BIRD_SIZE = 40; // Game variables let bird = {x:0, y:0, vy:0, width:BIRD_SIZE, height:BIRD_SIZE}; let pipes = []; // each pipe: {x, top, bottom} let frame = 0; let score = 0; let bestScore = 0; let running = false; let gameOver = false; const overlay = document.getElementById('overlay'); const scoreDiv = document.getElementById('score'); const reset = () => { bird.x = canvas.width * 0.2; bird.y = canvas.height / 2; bird.vy = 0; pipes = []; frame = 0; score = 0; running = false; gameOver = false; overlay.textContent = 'Press SPACE to start'; overlay.style.display = 'flex'; scoreDiv.textContent = ''; }; const addPipe = () => { const topHeight = Math.random() * (canvas.height - PIPE_GAP - 100) + 50; const bottomY = topHeight + PIPE_GAP; pipes.push({ x: canvas.width, top: topHeight, bottom: bottomY, passed: false }); }; const update = () => { if (!running) return; // Bird physics bird.vy += GRAVITY; bird.y += bird.vy; // Prevent bird from going off-screen (top) if (bird.y < 0) { bird.y = 0; bird.vy = 0; } // Add pipes at intervals if (frame % PIPE_SPACING === 0) { addPipe(); } // Move pipes pipes.forEach(p => p.x -= PIPE_SPEED); // Remove off-screen pipes pipes = pipes.filter(p => p.x + 60 > 0); // Collision detection & scoring pipes.forEach(p => { // Score when bird passes pipe centre if (!p.passed && p.x + 60 < bird.x) { p.passed = true; score++; scoreDiv.textContent = `Score: ${score}`; } // Bird-rectangle collision const collides = ( bird.x + bird.width > p.x && bird.x < p.x + 60 && (bird.y < p.top || bird.y + bird.height > p.bottom) ); if (collides) endGame(); }); // Ground collision if (bird.y + bird.height > canvas.height) endGame(); frame++; }; const draw = () => { // Clear ctx.clearRect(0,0,canvas.width,canvas.height); // Draw bird (simple circle) ctx.fillStyle = '#FFEB3B'; ctx.beginPath(); ctx.arc(bird.x + bird.width/2, bird.y + bird.height/2, bird.width/2, 0, Math.PI*2); ctx.fill(); // Draw pipes ctx.fillStyle = '#4CAF50'; pipes.forEach(p => { // Top pipe ctx.fillRect(p.x, 0, 60, p.top); // Bottom pipe ctx.fillRect(p.x, p.bottom, 60, canvas.height - p.bottom); }); }; const loop = () => { update(); draw(); requestAnimationFrame(loop); }; requestAnimationFrame(loop); const flap = () => { if (!running) { running = true; overlay.style.display = 'none'; scoreDiv.textContent = 'Score: 0'; } bird.vy = FLAP_STRENGTH; }; const endGame = () => { running = false; gameOver = true; bestScore = Math.max(bestScore, score); overlay.innerHTML = ` <div>Game Over</div> <div>Score: ${score}</div> <div>Best: ${bestScore}</div> <div>Press SPACE to restart</div> `; overlay.style.display = 'flex'; }; // Input handling window.addEventListener('keydown', e => { if (e.code === 'Space') { e.preventDefault(); if (gameOver) reset(); flap(); } }); // Mouse / touch also work as flap window.addEventListener('mousedown', e => { e.preventDefault(); if (gameOver) reset(); flap(); }); window.addEventListener('touchstart', e => { e.preventDefault(); if (gameOver) reset(); flap(); }); // Initial state reset(); })(); </script> </body> </html> ```