Drag the playfield on mobile · ←→ or mouse on desktop · Space launch · P pause · R restart
Canvas brick breaker · 400×600 · vanilla js
Drag the playfield on mobile · ←→ or mouse on desktop · Space launch · P pause · R restart
Under the hood
No physics engine, no dependencies — just a fixed 60 Hz simulation stepping a circle through a grid of axis-aligned boxes, four sub-steps at a time.
Accumulator loop drives update() at exactly 1/60 s, so behaviour is identical at 60 Hz and 144 Hz. Render runs free on requestAnimationFrame.
The ball's bounding box is tested against every brick cell, then the contact point is clamped to the box and measured against the true radius — no sloppy square-vs-square guesses.
Smaller overlap decides the axis: vertical overlap flips dy, horizontal flips dx. The ball is also pushed clear of the brick so it can never tunnel or nest.
Of every cell touched in a frame, only the deepest is resolved — then the loop breaks. No phantom double-breaks, no lost velocity.
Magnitude is renormalised after every reflection and clamped to 3–4 px/frame, so a corner hit can't accelerate the ball into orbit.
Contact offset maps to a bounce angle up to 55° off vertical, plus a whisper of paddle momentum. Centre hits still go straight up.
| Parameter | Value |
|---|---|
| Canvas | 400 × 600 |
| Brick grid | 8 × 4 · 50 × 20 |
| Brick count | 32 · max 320 pts |
| Paddle | 60 × 10 @ y 580 |
| Paddle travel | x 0 → 340 |
| Ball radius | 3 px |
| Ball spawn | (200, 570) |
| Launch vector | (+2.00, −2.80) |
| Speed clamp | 3.00 → 4.00 px/f |
| Lives | 3 |
| Sub-steps | 4 / frame |
| Dependencies | 0 |