Make a Flappy Bird game in Python, rendered via my web browser and served with Flask
<|channel>thought Flappy Bird game. Python. Flask. Web browser (HTML/CSS/JS).
* *Backend:* Flask (Python) to serve the HTML file and static assets.
* *Frontend:* HTML5 Canvas for rendering, JavaScript for game logic (physics, collision, input handling). This is crucial because real-time games like Flappy Bird are best handled on the client side to avoid latency issues, while Flask acts as the server.
* `app.py`: The Flask server.
* `templates/index.html`: The main structure.
* `static/style.css`: Styling for centering the game.
* `static/game.js`: The game engine logic.
* *Step 1: Flask Setup (`app.py`)*
* Import Flask.
* Define a route for `/` that returns `index.html`.
* *Step 2: HTML (`templates/index.html`)*
* Need a `<canvas>` element.
* Link the CSS and JS files.
* *Step 3: CSS (`static/style.css`)*
* Center the canvas.
* Give a border.
* *Step 4: JavaScript Logic (`static/game.js` - The hardest part)*
* *Variables:* Bird (x, y, velocity, gravity), Pipes (array of objects), Score, Game State.
* *Game Loop:* `requestAnimationFrame`.
* *Physics:* Gravity pulls bird down, jump on click.
* *Pipes:* Generate random height, move left, remove when off screen.
* *Collision:* Check if bird hits pipes, floor, or ceiling.
* *Drawing:* Draw bird and pipes every frame.
* *Bird:* A square (for simplicity) or a circle.
* *Pipes:* Rectangles with a gap.
* *Input:* `Space` bar or `click`.
* *Issue:* If I just write a basic loop, it might be too fast on different monitors.
* *Fix:* Use a delta time or a fixed-step interval approach to ensure the game runs at the same speed regardless of refresh rate.
* *Issue:* Collision detection can be tricky.
* *Fix:* Use simple axis-aligned bounding box (AABB) logic.<channel|>To create a Flappy Bird game served via Flask, we will use **Flask** as the web server and **HTML5 Canvas with JavaScript** for the game engine. Real-time game logic is best handled on the client-side (JS) to prevent input lag, while Python handles serving the files.
Here is the complete folder structure and the code to get it running.
flappy_bird/
│ app.py
├── templates/
│ └── index.html
└── static/
├── style.css
└── game.js
This script initializes the server and serves our HTML page.
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)This sets up the canvas where the game will be drawn.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flappy Bird Flask</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<div id="game-container">
<canvas id="gameCanvas" width="320" height="480"></canvas>
</div>
<script src="{{ url_for('static', filename='game.js') }}"></script>
</body>
</html>This centers the game on your screen.
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #223;
font-family: Arial, sans-serif;
}
#game-container {
border: 5px solid #fff;
}
canvas {
background-color: #70c5ce;
display: block;
}This handles the physics, collisions, and rendering.
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game variables
let bird = {
x: 50,
y: 150,
width: 20,
height: 20,
gravity: 0.6,
jump: -10,
velocity: 0
};
let pipes = [];
let pipeWidth = 50;
let pipeGap = 120;
let frameCount = 0;
let score = 0;
let gameOver = false;
// Controls
document.addEventListener('keydown', (e) => {
if (e.code === 'Space') flap();
if (gameOver) resetGame();
});
canvas.addEventListener('mousedown', flap);
function flap() {
if (gameOver) {
resetGame();
} else {
bird.velocity = bird.jump;
}
}
function resetGame() {
bird.y = 150;
bird.velocity = 0;
pipes = [];
score = 0;
frameCount = 0;
gameOver = false;
loop();
}
function createPipe() {
let minHeight = 50;
let maxHeight = canvas.height - pipeGap - minHeight;
let height = Math.floor(Math.random() * (maxHeight - minHeight + 1)) + minHeight;
pipes.push({
x: canvas.width,
top: height,
bottom: canvas.height - height - pipeGap
});
}
function update() {
if (gameOver) return;
bird.velocity += bird.gravity;
bird.y += bird.velocity;
// Pipe logic
if (frameCount % 90 === 0) {
createPipe();
}
for (let i = 0; i < pipes.length; i++) {
pipes[i].x -= 2;
// Collision detection
if (
bird.x + bird.width > pipes[i].x &&
bird.x < pipes[i].x + pipeWidth &&
(bird.y < pipes[i].top || bird.y + bird.height > canvas.height - pipes[i].bottom)
) {
gameOver = true;
}
if (pipes[i].x + pipeWidth < 0) {
pipes.shift();
score++;
}
}
// Floor and Ceiling collision
if (bird.y + bird.height > canvas.height || bird.y < 0) {
gameOver = true;
}
frameCount++;
}
function draw() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw Bird
ctx.fillStyle = "yellow";
ctx.fillRect(bird.x, bird.y, bird.width, bird.height);
// Draw Pipes
ctx.fillStyle = "green";
pipes.forEach(pipe => {
// Top pipe
ctx.fillRect(pipe.x, 0, pipeWidth, pipe.top);
// Bottom pipe
ctx.fillRect(pipe.x, canvas.height - pipe.bottom, pipeWidth, pipe.bottom);
});
// Draw Score
ctx.fillStyle = "white";
ctx.font = "20px Arial";
ctx.fillText(`Score: ${score}`, 10, 25);
if (gameOver) {
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "white";
ctx.fillText("GAME OVER", 80, 240);
ctx.fillText("Press Space to Restart", 70, 270);
}
}
function loop() {
update();
draw();
if (!gameOver) {
requestAnimationFrame(loop);
}
}
loop();- Install Flask:
pip install flask
- Create the files: Organize the code into the folder structure shown at the top.
- Run the app:
python app.py
- Play: Open your web browser and go to
http://127.0.0.1:5000.
How to play:
- Spacebar or Click to jump.
- Avoid the green pipes.
- Press Space to restart after losing.
total time: 137895.79ms, time per step: 1121.10ms (123 steps over 9 blocks, entropy-bound)