# Beat Lab — a drum machine and rhythm game (★★)

Build a 16-step drum machine in the browser. Four rows (kick, snare, closed hat, clap), sixteen buttons each. Press play and the pattern loops. Every sound is made from scratch with the Web Audio API, from oscillators and noise, with no sample files. Tempo and swing sliders change the groove. The whole beat is saved in the page address, so sending the link sends the beat. Then add a rhythm game mode: the beat plays, notes from one row scroll towards a line, and you tap in time and get an accuracy score.

## Done looks like

- A 4 by 16 grid of step buttons, a play/stop button, tempo (60 to 180 BPM) and swing (0 to 50 percent) controls, and a volume and mute per row.
- Kick, snare, closed hat and clap are all synthesized. No audio files in the project.
- Timing stays tight: at 120 BPM the loop does not drift or stutter, even while you move the sliders or switch tabs briefly.
- A playhead highlights the current step, in time with what you hear.
- Add-on: the address bar holds the pattern, tempo and swing (for example `#v1-t120-s20-` then hex). Opening that link in another browser plays the same beat.
- Rhythm mode: pick a row, notes scroll to a hit line, tap (Space, or a big pad on a phone). Each tap is judged Perfect, Good or Miss. A results screen shows accuracy percent and best combo.
- A calibration screen measures your device's delay so taps are judged fairly.
- Works on a phone. No console errors.

## Constraints

- Static files, no build step: `index.html`, `style.css`, `audio.js`, `app.js`. No frameworks, no audio libraries.
- Sounds: kick is a sine oscillator whose pitch drops from about 150 Hz to 50 Hz in 0.1 s with a fast gain envelope. Snare is white noise through a high-pass filter mixed with a short triangle tone. Closed hat is noise through a high-pass filter around 7 kHz with a 50 ms decay. Clap is three quick noise bursts through a band-pass filter.
- Scheduling: never play sounds straight from `setInterval` or `setTimeout`. Use a lookahead scheduler: a timer every 25 ms schedules every step due in the next 100 ms at exact `AudioContext.currentTime` values. The drawing loop (`requestAnimationFrame`) reads a queue of scheduled steps to move the playhead.
- Swing: every second sixteenth note is delayed by `swing × step length`, with swing from 0 to 0.5.
- The `AudioContext` is created or resumed on the first tap or click, because phones block sound until then.
- Pattern in the URL hash, updated with `history.replaceState` as you edit (no new history entry per click). Each row is 16 bits written as 4 hex characters.
- Rhythm judging uses the audio clock, not `Date.now()`. Perfect within 40 ms, Good within 90 ms, otherwise Miss. The calibration offset is saved in localStorage.
- No server, no tracking, no accounts.
- Phone works: buttons at least 40 px, the grid scrolls inside its own box if it must, never the whole page sideways.

## Milestones

1. **One sound.** A button that plays a synthesized kick. Create the `AudioContext` on first tap.
   Check: tapping the button on a phone plays a punchy kick every time, with no delay you can hear.
2. **Four drums.** Add snare, closed hat and clap as functions that take a start time. A test row of four buttons plays each.
   Check: each button plays a sound that is clearly a snare, a hat or a clap, not just a beep.
3. **Grid and scheduler.** The 4 by 16 grid and the lookahead scheduler at a fixed 120 BPM. Clicking a step toggles it while playing.
   Check: a four-on-the-floor kick with snares on steps 5 and 13 loops for two minutes without drifting, while you click other steps.
4. **Tempo, swing, playhead, mix.** Tempo and swing sliders that change the groove on the next step. A playhead synced from the scheduled-steps queue. Volume and mute per row.
   Check: dragging swing to 50 percent turns straight hats into a shuffle, and the playhead stays exactly on the beat you hear.
5. **Rhythm mode.** Pick a row. Its notes scroll down to a hit line, timed from the audio clock. Tap to hit. Judge Perfect, Good, Miss. Show combo and a results screen with accuracy.
   Check: tapping exactly on the notes gives mostly Perfect; tapping randomly gives mostly Miss.
6. **Calibration and phone pass.** A calibration screen: a click plays 8 times, you tap along, the average offset is saved and used in judging. Check layout on a real phone.
   Check: after calibrating, your accuracy on a simple pattern goes up; the whole app works on a phone with sound on.
> That is the whole instrument and the game. Sharing comes last, once you have a beat worth sending.
7. **Share by link.** Encode pattern, tempo and swing in the hash. Load from the hash on open. A "Copy link" button.
   Check: build a beat, copy the link, open it in a different browser: the same beat, tempo and swing play.

## Stretch

- Two more rows: low and high toms with adjustable pitch.
- Pattern A and B with a chain button, so you get a 32-step loop.
- Accent per step (louder hit) with a long press.
- Export a WAV of four loops using `OfflineAudioContext`.
- A daily rhythm challenge pattern, seeded by the date.
- Play the pads from a MIDI keyboard with Web MIDI, where the browser supports it.

## Leave out

- Sample files, Tone.js or any audio library.
- A server, accounts, saving beats online.
- A full music editor: no piano roll, no song mode beyond A and B.

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