01Penetration axes
The brick AABB is inflated by the ball radius. Both overlap depths are measured — ox vs oy — and the shallower one wins, so the ball is pushed back out along the face it actually entered. Side hit flips dx, top/bottom flips dy.
02One hit per step
Each of the two sub-steps accepts only the shallowest-penetrating brick, so a ball crossing a seam never scores twice. Positional correction runs after every bounce — the ball can never end a frame inside a brick.
03Paddle angle map
Hit offset from paddle centre normalises to -1…1 and maps to ±60°. Dead centre sends it straight up, edges throw it hard into the wall.
04Speed budget
Magnitude is re-normalised after every bounce — bounces rotate the vector, never stretch it. Base 3.44 px/frame climbs to 4.0 as the wall thins, sub-stepped so nothing tunnels.
05Input mapping
Mouse, keys and touch all feed one smoothed target. Screen coordinates are remapped from CSS size back to the 400×600 logical field, so a 320 px phone gets the same paddle feel as a desktop.
06Anti-stall
If nothing is touched for five seconds the vector tilts gently back toward vertical, killing the endless horizontal ping-pong between side walls. Fixed 1/60 s accumulator keeps physics identical on 120 Hz panels.