No engine, no math library — just a fixed 60 Hz accumulator and explicit collision maths on a 400 × 600 backing store.
01Minimum-penetration side resolution
For every ball↔brick overlap we measure ox = (w/2 + r) − |Δcx| and oy = (h/2 + r) − |Δcy|, then flip the axis with the smaller overlap — top/bottom flips dy, sides flip dx — and eject the ball by exactly that depth so it never re-enters the block.
02Sub-stepped sweep
Motion is split into ceil(speed / 2) sub-steps. Only the closest overlapping brick is processed per step, and a last-brick lockout ID blocks double-hits in the same frame.
03Paddle angle map
Contact offset normalises to −1…1 across the 60 px bat and maps to ±62°. Centre hits go near-vertical, edge hits skim — and a side strike reflects dx instead. Velocity magnitude is renormalised after every bounce.
04Frame-rate independence
Physics runs on a fixed 1000/60 accumulator (max 5 catch-up steps), so 120 Hz phones and 144 Hz monitors get identical trajectories. The canvas backing store is scaled by devicePixelRatio while logic stays in 400 × 600 space; touch and pointer input map through the CSS rect.