# Tumble — a 3D obstacle race against bots (★★★)

Build a one-level obstacle race in the browser, in 3D. Twelve jelly-bean runners start together on a floating course. They run through spinning bars, hop across platforms that slide over a gap, cross a narrow bridge under swinging balls, and climb a ramp with balls rolling down it. The first six over the line qualify. You steer one bean; the other eleven are bots. It plays with a keyboard on a desktop and with a thumb stick and a Jump button on a phone.

## Done looks like

- A menu card over the live course: the name, one line on the goal, a Play button, the controls for the device in hand, and your best time.
- Play counts 3, 2, 1, GO. Nobody can move until GO.
- One course, about 170 units long, in five parts: a start pad; a wide floor with three spinning bars to jump; three platforms sliding left and right over a gap; a narrow bridge with three swinging balls; a ramp with balls rolling down five lanes, then the finish arch.
- Getting hit throws your bean and stuns it for about half a second. Falling off puts you back at the last checkpoint. There are four checkpoints, one at the start of each part.
- Eleven bots that play the same course by the same rules. They jump the bars, wait for a platform to line up, wait for a ball to pass and step out of a roller's lane. Each has a skill number, so some are good and some fall off a lot.
- Beans bump into each other.
- At the top of the screen: your place (1st to 12th), how many are through out of six, the time, and a bar showing how far along the course you are.
- The round ends when you cross the line (Qualified, with your place and time, and confetti) or when six bots cross before you (Eliminated, with your place). Then a Play again button.
- Your best qualifying time is kept in the browser.
- Works on a phone held upright and on a desktop. No console errors.

## Constraints

- Static files, no build step: `index.html`, `style.css`, `level.js`, `beans.js`, `main.js`.
- Three.js for drawing, loaded by an import map from a pinned version on a CDN. Never "latest". No physics library and no other library.
- Physics is your own and small: a bean is a point with a velocity; gravity pulls it down; floors are boxes with a top height (the ramp's top rises along its length); hazards are tested by distance. Run it on a fixed step of 1/60 second, however fast the screen draws.
- The course is data first: a list of platforms (centre, width, depth, height, and how it moves), a list of bars, a list of pendulums, a list of checkpoints. The picture, the physics and the bots all read the same lists.
- Controls: arrow keys or WASD, Space to jump. On touch: drag anywhere on the left of the screen for a stick that appears under the thumb, and a Jump button at least 96 px across on the right. Jumping forgives a little: a press up to a tenth of a second early or late still counts.
- Camera: behind and above your bean, always looking down the course. It does not turn. Pull it back and widen it when the screen is taller than it is wide.
- The level's movers keep moving behind the menu, but beans take no input until the countdown ends.
- Sounds are short oscillator beeps made in code, with a sound switch. The `AudioContext` is created on the Play tap.
- Best time and the sound switch live in localStorage under a `tumble:` prefix.
- The Play button says Loading and is disabled until the script has run.
- No lights that cast shadows: a dark circle under each bean is enough and keeps phones fast. Device pixel ratio capped at 2.
- Look: bright candy colours, striped floors, a sky-blue background with clouds and pillars far off, rounded heavy type with thick dark outlines.
- No server, no tracking, no accounts.

## Milestones

1. **A bean on a floor.** A Three.js scene with a sky, a light, the start pad and one bean you can run and jump with the keyboard. The camera follows.
   Check: run off the edge and you fall; you come back on the pad.
2. **The whole course.** All eight platforms from a list, the three sliders moving, the ramp sloping, the finish arch. Checkpoints.
   Check: you can run from start to finish with nothing in the way, riding a slider carries you with it, and falling at the bridge brings you back to the bridge.
3. **Things that hit.** Spinning bars, swinging balls, rolling balls. A hit throws and stuns.
   Check: stand in front of each hazard and get hit; jump a bar cleanly; a ball on the ramp knocks you back down the slope.
4. **Bots.** Eleven more beans driven by a function that returns the same stick-and-jump input you give. Beans push each other apart.
   Check: start a race and leave your hands off. Most bots finish in 25 to 40 seconds and a few fall along the way.
5. **A race.** Countdown, place, timer, progress bar, Qualified and Eliminated, Play again, best time.
   Check: nobody moves before GO; lose once on purpose and win once; the best time survives a reload.
> That is the whole race. The next steps make it work on a phone and feel good.
6. **Phone.** Thumb stick, Jump button, upright camera, safe areas.
   Check: play a full round on a real phone with two thumbs. The page does not scroll or zoom while you play.
7. **Juice.** Squash on jump and landing, feet that run, a spin when stunned, a screen shake on a hit, confetti, sounds.
   Check: turn the sound off and the game still reads; turn it on and every hit, jump and checkpoint has a sound.

## Stretch

- A second course, and three rounds in a row with fewer qualifying each time.
- Pick your bean's colour.
- A ghost of your best run.

## Leave out

- Real multiplayer. That is the next project, Arena.
- Grabbing and diving.
- A physics engine.

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