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
The bird never moves horizontally — the world scrolls left instead. Keeping the bird at a fixed x-coordinate simplifies collision detection considerably.

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
Gap position is randomised between 20 % and 70 % of canvas height so it's always reachable but never trivially easy.

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.

score: 0 difficulty:
space or ↑ to flap · pass pipes to score

Concepts at a Glance

ConceptImplementation
Physicsvy += gravity each frame; tap sets vy to FLAP_VEL
Game looprequestAnimationFrame, cancelled on game over
PipesArray of {x, gapTop, scored}, scrolled left each frame
Inputkeydown (Space / ↑) + Flap button + canvas tap
Renderingcanvas.getContext("2d") draw calls each frame
CollisionFloor/ceiling bounds + per-pipe AABB vs circle
ScoringFlag set when bird x passes pipe right edge

[go back to /cool stuff]