# Dodge — a one-file canvas game (★)

Build a small arcade game in one `index.html`. You are a dot near the bottom of the screen. Shapes fall from the top. Touch one and the run is over. The longer you last, the faster they fall and the more of them there are. Your score climbs every second you survive, and your best score is kept on this device. It plays with a thumb on a phone and with the keyboard on a laptop. Losing should look and feel good, so the player hits restart straight away.

## Done looks like

- Opening `index.html` shows a title screen. A tap, Space or Enter starts a run.
- The dot moves with the arrow keys or A and D. On a phone, dragging a finger anywhere moves the dot to follow the finger left and right.
- Squares, triangles and circles fall at different sizes and speeds. Speed and spawn rate rise smoothly for the first 90 seconds, then level off at "very hard".
- A hit ends the run: a particle burst from the dot, a short screen shake, then a game over screen with the score and the best score.
- Add-on: the best score survives a page reload (localStorage key `dodge.best`).
- Restart works with a tap, Space or Enter, without reloading the page.
- It runs at 60 frames per second on a mid-range phone. The game speed is the same on a 60 Hz and a 120 Hz screen.
- Switching tabs pauses the game. Coming back does not kill you.
- The browser console shows no errors.

## Constraints

- One file: `index.html` with inline `<style>` and `<script>`. No libraries, no build step, no image or sound files.
- Canvas 2D. One `requestAnimationFrame` loop. Every movement is scaled by delta time (seconds since the last frame), capped at 0.05 s or less so a slow frame cannot teleport anything.
- The canvas fills the window, is sharp on high-DPI screens (`devicePixelRatio`) and handles resize and phone rotation.
- Input uses pointer events and keyboard events. The canvas has `touch-action: none` so the page does not scroll or zoom while playing.
- Sizes and speeds are based on the screen size, so a phone and a laptop feel the same.
- Works offline. Nothing is sent anywhere. No tracking, no accounts.
- Plain code with short comments. A 15-year-old should be able to read it top to bottom.
- Phone works: test on a real phone before calling it done.

## Milestones

1. **Canvas and loop.** A full-window canvas, a dark background, a white dot near the bottom centre. A `requestAnimationFrame` loop with delta time, and a small frames-per-second counter in a corner.
   Check: the counter shows about 60 and the dot sits still at the bottom centre, sharp, not blurry.
2. **Move.** Arrow keys, A and D, and pointer drag move the dot. It speeds up and slows down smoothly instead of jumping, and it stops at the screen edges.
   Check: the dot glides left and right with the keys and follows your finger on a phone without the page scrolling.
3. **Falling shapes.** Spawn shapes at a random x above the top edge. They fall, spin a little, and are removed once they leave the bottom.
   Check: a steady rain of shapes, and a live-shapes count shown next to the frame counter stays roughly flat instead of climbing forever.
4. **Hit and score.** Test circles as circles and squares and bars as rotated rectangles, each a little smaller than drawn, so near misses feel fair. Score is whole seconds survived, plus 5 for each near miss (passing within a few pixels of a shape), shown at the top. A hit stops the run and shows a game over screen with the score.
   Check: touching a shape ends the run and shows your score; brushing past a corner does not.
5. **Ramp and restart.** Speed and spawn rate rise smoothly with time. Add a title screen, a game over screen and restart without reload. Keep the best score of this session in memory.
   Check: after 30 seconds the game is clearly harder, and restart works without a reload.
6. **Juice.** On death, 40 to 60 particles burst from the dot with random velocity, gravity and fade-out. The screen shakes and the shake dies away over 0.3 s. The dot squashes sideways when it changes direction and stretches back. A short flash and "New best" text when you beat your best so far.
   Check: dying looks like a small explosion, the screen shakes and then settles, and the dot visibly squashes when you turn.
7. **Pause and phone pass.** Pause on `visibilitychange`, show "Paused, tap to go on", and reset the frame timer on resume. Make text readable on a small screen. Hide the frame and shape counters unless the page is opened with `?debug=1`. Play three full runs on a real phone.
   Check: switch tabs in the middle of a run and come back: the game is paused, not over; the counters are gone unless `?debug=1` is in the address; three runs on a phone with no stutter.
> Everything above is the whole game. Play it for a while before going on.
8. **Remember the best score.** Save the best score in localStorage under `dodge.best` and show it on the title and game over screens.
   Check: reload the page and your best score is still shown.

## Stretch

- Near-miss bonus: extra points and a small spark when a shape passes very close.
- A slow-time power-up that falls now and then and lasts three seconds.
- Sound with Web Audio: a soft blip on near misses and a crunch on death, made with oscillators, no files.
- A daily challenge where everyone gets the same shape pattern, using a random number generator seeded by the date.
- Colour themes you earn by reaching score milestones.
- A ghost trail that shows where you moved on your best run.

## Leave out

- Menus beyond title and game over, settings pages, accounts, online leaderboards.
- Frameworks, game engines, sprite sheets or any asset files.
- Ads, analytics, anything that talks to a server.

Start by reading WRITING.md and the guide chapter on data if you need storage.
