Flappy Bird
One of the most addictive games ever made. Tap to flap and keep the bird airborne while squeezing through gaps between pipes. Gravity pulls you down constantly — every tap is a tiny burst of upward velocity against an endless fall. Simple rules, brutally hard.
Physics: Gravity and Velocity
The bird has a single vertical position y and a vertical velocity vy.
Every frame, gravity is added to vy, which is then added to y.
A tap sets vy to a fixed negative (upward) value — that's the entire physics model.
const GRAVITY = 0.35 // added to vy each frame
const FLAP_VEL = -6.5 // vy on tap (negative = up)
vy += GRAVITY
y += vy
Pipes: Scrolling Obstacles
Pipes spawn at the right edge at a random gap position and scroll left at a fixed speed. Each pipe is just two rectangles — a top slab and a bottom slab — with a gap in between. When a pipe leaves the left edge it is removed; a new one is queued at a fixed interval.
const PIPE_SPEED = 2.4 // pixels per frame
const PIPE_WIDTH = 30 // px
const GAP_HEIGHT = 110 // px between top and bottom pipe
const PIPE_EVERY = 90 // frames between spawns
pipe.x -= PIPE_SPEED // scroll each frame
if (pipe.x + PIPE_WIDTH < 0) pipes.shift() // cull
Collision Detection
The game ends in two cases, both checked every frame before drawing.
// Floor / ceiling collision
if (bird.y - BIRD_R < 0 || bird.y + BIRD_R > H) die()
// Pipe collision (AABB vs circle approximation)
const inX = bird.x + BIRD_R > pipe.x &&
bird.x - BIRD_R < pipe.x + PIPE_WIDTH
const inGap = bird.y - BIRD_R > pipe.gapTop &&
bird.y + BIRD_R < pipe.gapTop + GAP_HEIGHT
if (inX && !inGap) die()
Scoring
One point is awarded each time the bird's x-coordinate passes a pipe's right edge. A boolean flag on each pipe prevents double-counting.
if (!pipe.scored && bird.x > pipe.x + PIPE_WIDTH) {
pipe.scored = true
score++
}
Play
Space or ↑ on desktop. Flap button on mobile. Hit Start to play.
Concepts at a Glance
| Concept | Implementation |
|---|---|
| Physics | vy += gravity each frame; tap sets vy to FLAP_VEL |
| Game loop | requestAnimationFrame, cancelled on game over |
| Pipes | Array of {x, gapTop, scored}, scrolled left each frame |
| Input | keydown (Space / ↑) + Flap button + canvas tap |
| Rendering | canvas.getContext("2d") draw calls each frame |
| Collision | Floor/ceiling bounds + per-pipe AABB vs circle |
| Scoring | Flag set when bird x passes pipe right edge |