Creating a Flappy Bird Clone with p5.js: A Step-by-Step Guide
Flappy Bird took the mobile gaming world by storm in 2013 with its simple yet addictive gameplay. In this tutorial, we’ll recreate this classic game using p5.js, a JavaScript library perfect for creative coding. By the end, you’ll have a fully functional Flappy Bird clone running in your browser, complete with animated sprites, pipes, and a scoring system—all within a single HTML file!
What You’ll Need
- Basic knowledge of HTML and JavaScript.
- A text editor (e.g., VS Code, Notepad++).
- Three assets: a bird sprite sheet, a pipe image, and a background image (more on sourcing these below).
- A web browser to test your game.
Finding the Assets
To make our game visually appealing, we’ll use free assets available online. Here’s where to get them:
- Bird Sprite Sheet: A 128×32 PNG with 4 frames (each 32×32 pixels) for animation. Search for “Flappy Bird sprite sheet” on sites like OpenGameArt or The Spriters Resource.
- Pipe Image: A vertical pipe PNG. Check PNGkey or similar sites for a free option.
- Background Image: A blue sky with clouds. Look on PNGEgg or Pixabay.
Once downloaded, upload these to a hosting service like GitHub to get direct URLs (e.g., https://raw.githubusercontent.com/yourusername/flappy-bird-assets/main/bird.png
). Replace the placeholder URLs in the code below with these links.
The Code
Below is the complete code for our Flappy Bird clone, written as a single HTML file using p5.js. Copy this into a file named flappy-bird.html
, update the image URLs, and open it in a browser to play!
<html>
<head>
<title>Flappy Bird Clone</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.js"></script>
<script>
let birdImg, pipeImg, backgroundImg;
let bird;
let pipes = [];
let score = 0;
function preload() {
// Replace these with your actual image URLs
birdImg = loadImage('https://example.com/bird.png');
pipeImg = loadImage('https://example.com/pipe.png');
backgroundImg = loadImage('https://example.com/background.png');
}
function setup() {
createCanvas(400, 600);
bird = new Bird();
pipes.push(new Pipe());
}
function draw() {
background(backgroundImg);
bird.update();
bird.show();
if (frameCount % 100 == 0) {
pipes.push(new Pipe());
}
let hit = false;
for (let i = pipes.length - 1; i >= 0; i--) {
pipes[i].show();
pipes[i].update();
if (pipes[i].hits(bird)) {
hit = true;
}
if (pipes[i].pass(bird)) {
score++;
}
if (pipes[i].offscreen()) {
pipes.splice(i, 1);
}
}
if (bird.y > height) {
hit = true;
}
if (hit) {
console.log("Game Over");
noLoop();
}
textSize(32);
fill(255);
text(score, 10, 30);
}
function mousePressed() {
bird.flap();
}
function keyPressed() {
if (key == ' ') {
bird.flap();
}
}
class Bird {
constructor() {
this.x = 50;
this.y = height / 2;
this.velocity = 0;
this.gravity = 0.6;
this.lift = -15;
this.size = 32;
}
show() {
let frame = floor(frameCount / 5) % 4;
image(birdImg, this.x, this.y, this.size, this.size, frame * 32, 0, 32, 32);
}
update() {
this.velocity += this.gravity;
this.y += this.velocity;
if (this.y > height) {
this.y = height;
this.velocity = 0;
}
}
flap() {
this.velocity = this.lift;
}
}
class Pipe {
constructor() {
this.spacing = 175;
this.topHeight = random(50, height - 50 - this.spacing);
this.x = width;
this.w = 80;
this.speed = 2;
this.passed = false;
}
show() {
image(pipeImg, this.x, 0, this.w, this.topHeight);
let bottomY = this.topHeight + this.spacing;
image(pipeImg, this.x, bottomY, this.w, height - bottomY);
}
update() {
this.x -= this.speed;
}
offscreen() {
return this.x < -this.w; } hits(bird) { if (bird.x + bird.size/2 > this.x && bird.x - bird.size/2 < this.x + this.w) {
if (bird.y - bird.size/2 < this.topHeight || bird.y + bird.size/2 > this.topHeight + this.spacing) {
return true;
}
}
return false;
}
pass(bird) {
if (bird.x > this.x + this.w && !this.passed) {
this.passed = true;
return true;
}
return false;
}
}
</script>
</body>
</html>
How It Works
- Preload: Loads the bird sprite sheet, pipe, and background images using
loadImage()
. - Setup: Creates a 400×600 canvas and initializes the bird and first pipe.
- Draw: Updates and renders the game state—moves the bird, generates pipes every 100 frames, checks for collisions, and updates the score.
- Bird Class: Handles gravity, flapping (via mouse or spacebar), and animation using the sprite sheet’s 4 frames.
- Pipe Class: Manages pipe movement, collision detection, and scoring when the bird passes through.
Gameplay
Click the mouse or press the spacebar to flap the bird’s wings. Navigate through the gaps between pipes, and watch your score increase as you pass each pair. If you hit a pipe or the ground, the game stops, and “Game Over” appears in the console.
Customizing the Game
Feel free to tweak the code:
- Adjust
this.gravity
orthis.lift
in theBird
class for different difficulty levels. - Change
this.spacing
in thePipe
class to make gaps larger or smaller. - Add sound effects by including audio files and using p5.js’s
loadSound()
andplay()
functions.
Conclusion
You’ve just built a Flappy Bird clone with p5.js! This project is a great way to practice JavaScript classes, animation, and game logic. Host it on GitHub Pages or your WordPress site by embedding the HTML file in a custom HTML block. Happy coding, and let me know how it goes in the comments!
Published on February 20, 2025