tap the screen to serve — the paddle follows your finger
32 cyan blocks. one yellow ball. three lives. Explicit AABB collision, closest-hit resolution, fixed 60 Hz physics step — no engines, no libraries.
tap the screen to serve — the paddle follows your finger
Every collision below is written by hand. The rule that keeps the board honest: one resolution per frame — the closest penetration wins, the rest wait for the next tick.
Eight columns, four rows, laid out top-aligned with no gutter — brick i lives exactly at x = col*50, y = row*20.
400×600 · 8×4 · 50×20Seeded from dx=2, dy=-2.8, normalised, then jittered ±11° so no two serves are identical — never straight up.
|v| = 3.44 → 4.00 px/fHit position across the 60px face maps to a ±60° exit angle. Centre bounces up, edges fling it wide — and the vertical floor stops horizontal lock.
rel ∈ [-1,1] → sin/cosClosest-point test against the brick rect, then four penetration depths. The smallest decides the axis: top/bottom flips dy, left/right flips dx.
closest hit wins · 1/frameOn resolution the ball is repositioned flush outside the brick, so it can never re-enter the same cell — and velocity is re-normalised to constant magnitude every step.
clamp + renormalise+10 a brick, 320 max. Three drops ends it. Paddle is clamped to 0–340 so the side walls are always sealed — no sneaky corner escapes.
3 lives · win @ 32Finger x is remapped through getBoundingClientRect() into game space, smoothed toward the paddle, with touch-action:none so the page never scrolls mid-rally.
ontouchstart / move / endAccumulator loop with a fixed 16.66 ms step (capped at 5 catch-up steps), so a 120 Hz phone and a 60 Hz monitor play identically.
requestAnimationFrame