Thirty-two bricks of phosphor, three lives, and one yellow ball travelling at 3.4 pixels per frame. Clear the wall.
The ball is treated as a 6 px AABB sweeping through the field in sub-steps of no more than 2 px. When it overlaps more than one brick in a single frame, the engine measures penetration depth on both axes, picks the shallowest (the closest, real contact) and resolves exactly one collision — the ball is then pushed clear of the brick, so it can never be re-detected inside the same box.
Launch vector is (2, −2.8) normalised to the speed constant — 35.5° off vertical. Subsequent serves jitter ±12° so the ball never repeats a straight-up trajectory.
Reflection is derived from horizontal offset, clamped to ±1.0 so the ball can never be reflected into a purely horizontal loop.
Resolve one collision per sub-step, then push the ball out to the brick face. Position correction happens before the next overlap test, so a brick can't be hit twice in a frame.
Ball speed (≤4 px) exceeds brick height (20 px) tolerance in the worst case, so motion is split into ceil(dist/2) sub-steps of at most 2 px each.
Flipping a component never changes the vector's length; the velocity is renormalised to the constant speed after every reflection.
Paddle x is hard-clamped to [0, W−60]. It can never leave the field, and the ball can never squeeze past it off-screen.
Multi-brick overlap resolves to minimal penetration depth, and the paddle test runs before the brick test — no phantom bounces on a stack collapse.
Delta time is folded into the step size (clamped 0.5–2 frames) so a 30 fps phone and a 120 fps monitor see identical physics.
Hold to slide the paddle at 7 px/frame. Touches the walls at 0 and 340.
Serve the ball from the paddle centre. Same as tapping the playfield.
Pause / resume. R restarts the run instantly.
Touch anywhere on the screen and slide — the paddle lerps to your finger at 35% per frame, so fast swipes stay smooth.