CREDIT 01 — INSERT COIN TO CONTINUE
HTML5 canvas · 400×600 field · 8×4 brick grid · 320 points max
The ball is tested as an expanded AABB (radius 3). On overlap the surviving brick closest to the ball centre wins — exactly one brick per frame — and the reflection axis is derived from the ball's previous sub-step position: enter from the left/right face → flip dx, enter top/bottom → flip dy. The ball is then pushed flush to that face, so it can never be left inside a brick.
Three sub-steps per 1/60 s tick, driven by an accumulator so a 120 Hz display does not run the game at double speed and a stalled frame cannot tunnel a 3 px ball through a 20 px brick. Velocity magnitude is re-normalised after every reflection — speed stays at 3.4 px/frame forever.
Contact offset (ball.x − paddleCentre) / 30 maps to a ±60° exit angle, with a small amount of paddle momentum added as English. Edge hits skim sideways, dead-centre hits bounce up; a shallow-angle guard keeps |dy| ≥ 0.9 so the ball never grinds horizontally. Side and underside contacts resolve as their own surfaces.
The canvas is CSS-fluid (works at 320 px width and in landscape), the backing store is scaled by devicePixelRatio capped at 2, and all input maps client coordinates back into the 400×600 logical space. Touch drag sets a paddle target that is lerped for smoothness; a short, still tap is treated as launch / restart instead of a drag.
Score, lives, brick progress, overlays and the restart button are all Canvas drawing with hit-tested rectangles. The static background is pre-rendered once into an off-screen buffer; the rest of the frame is ~30 bricks, a particle pool, a ball trail and a light sweep — comfortably inside a 16 ms budget on mobile.