# Pulse — a four-lane rhythm game (★★)

Build a rhythm game in the browser. Four lanes, notes fall towards a line, and you tap the lane (D F J K on a keyboard, the lane itself on a phone) as each note crosses the line. Some notes are holds: press on the head and let go on the tail. Every tap is judged Perfect, Great or Miss, a combo multiplies the score, and the round ends with a grade from D to S. Three songs are written as text and played by the Web Audio API with no sound files, and the notes you play are built from the melody, so what you see is what you hear. The screen pulses on every beat, so the game is playable with the sound off.

## Done looks like

- A menu with three songs, three difficulties (Easy, Normal, Hard), a Play button and the best score for the pick you made.
- A play screen: four lanes with their own colours, notes falling to a hit line, a pad under each lane that lights while you press it, a score at the top, a progress bar, and a Pause button.
- Judging: Perfect within 50 ms of the note, Great within 110 ms, Miss beyond 150 ms or never pressed. A press with no note nearby does nothing.
- Hold notes: judged on the head like a tap; the tail is a Perfect if you keep holding to within 150 ms of its end, a Miss if you let go early.
- Score: Perfect 100, Great 50, times a multiplier that grows with the combo to 2x at 50. Accuracy is Perfect as 1, Great as half, over every judgement. Grade S at 95 percent, A at 90, B at 80, C at 65, else D.
- Visual cues that work without sound: the hit line and the lane backgrounds pulse on every beat, stronger on the first beat of the bar; a count-in shows 3, 2, 1 on the three beats before the song and GO on its first beat; a hit draws a ring and a burst of sparks in the lane's colour; the judgement word pops up and fades, with "early" or "late" under a Great; the combo number sits large in the middle from 5 up and jumps on every hit; every 25 combo flashes a banner and shakes the screen a little.
- A results screen: the grade in huge type, score counting up, accuracy, best combo, the three counts, a small bar chart of how early or late your taps were, and one line of advice from it. New best scores are kept in the browser per song and difficulty.
- Settings: scroll speed 1 to 5, a sound switch, and a timing offset with a Calibrate screen (a ring flashes eight times with a click, you tap along, the average lateness is saved and taken off every press).
- Works on a phone, with multi-touch so two lanes can be held at once. No console errors.

## Constraints

- Static files, no build step: `index.html`, `style.css`, `music.js`, `chart.js`, `game.js`. No frameworks, no audio or game libraries.
- Songs are data, not files. A song is a tempo, a string of letters (one per bar) and a part per letter. A part has a lead line, a bass line and three drum lines. Lead and bass use sixteen tokens per bar: a note like `c4` or `f#3` starts a note, `-` holds it one more sixteenth, `.` is a rest. Drums use `x` and `.`. Three songs: about 90 BPM with notes on the beat, about 125 BPM with eighth notes and holds, about 150 BPM with sixteenths.
- Sounds come from oscillators and noise: kick is a sine dropping from 150 Hz to 50 Hz, snare is filtered noise plus a short triangle, hat is noise through a high-pass filter, bass is a sawtooth through a low-pass filter that closes after the attack, lead is two detuned square waves through a low-pass filter. The count-in is a short sine click.
- Scheduling: a 25 ms timer queues every sound due in the next 120 ms at exact `AudioContext` times. Never play a sound straight from a timer or the drawing loop. The `AudioContext` is created on the first tap, because phones block sound until then. Sound off is a gain set to zero; the clock keeps running so the game still works.
- The chart is built from the song: each lead note is a tap in a lane chosen by pitch (the lowest quarter of the pitches go in lane 1, the highest in lane 4). A note three sixteenths or longer is a hold. Easy keeps only the notes that land on a beat and needs six sixteenths for a hold. Hard adds a tap on every kick drum hit that is not within a sixteenth of a melody note, and a second lane where the kick lands exactly on a melody tap.
- All timing comes from the audio clock: song time is `currentTime` minus the start time. Judging subtracts the saved offset from the press time. Pause suspends the `AudioContext`, which freezes its clock, and resume continues it.
- Drawing is one `requestAnimationFrame` loop on a canvas sized for the device pixel ratio. A note's y position is the hit line minus (note time minus song time) times pixels per second, where the top-to-line time is 2.4, 1.8, 1.35, 1.0 or 0.75 seconds for speeds 1 to 5.
- Keys: D F J K and the arrow keys. Touch: pointer events on the canvas, each pointer id tied to the lane it went down in, so two fingers work. Escape pauses.
- Settings, offset and best scores live in localStorage under a `pulse:` prefix.
- No server, no tracking, no accounts.
- Look: one deep dark background with a slow moving coloured glow, four bright lane colours that glow with `shadowBlur`, a gradient title and Play button, heavy type for every number that matters. Nothing grey and flat. Phone first: lanes fill the width up to 560 px and the pads are at least 44 px tall.

## Milestones

1. **A song from text.** Write the song format and the sound engine. A page with one Play button that plays the first song with the lookahead scheduler.
   Check: the song plays through on a phone after one tap, in time, with a kick you can feel and a melody you can hum.
2. **Lanes and falling notes.** Build the chart from the melody. Draw four lanes, the hit line and notes falling at the right speed, with a count-in of 3, 2, 1 and GO on the first beat.
   Check: with the sound on, every note crosses the line exactly as you hear it. With the sound off, the notes still fall and the lanes pulse on the beat.
3. **Tap and judge.** Keys and touch press a lane. Judge Perfect, Great and Miss by the audio clock. Draw the judgement word, the ring, the sparks and the lit pad. Score and combo.
   Check: tapping on the notes gives mostly Perfect, tapping randomly gives mostly Miss, and a press with no note nearby does nothing.
4. **Holds, Hard and the end.** Hold notes with a head and a tail. Hard difficulty adds the kick. A results screen with grade, counts, the early-or-late chart and advice. Pause and quit.
   Check: on Night Drive, holding through a long note scores its tail, letting go early shows Miss, and the round ends on a results screen with a grade.
5. **Menu, three songs, the look.** Song list with colours, difficulty chips, Play, speed and sound settings. Make it look like something you would show a friend: glow, gradients, big combo, screen shake on 25.
   Check: on a phone, the menu fits without sideways scrolling and the combo number is readable at arm's length.
> That is the whole game. The next steps remember things between visits.
6. **Best scores and calibration.** Keep the best score per song and difficulty. A Calibrate screen with eight flashes and clicks that saves the average offset, used in judging.
   Check: after a round, the menu shows your best. After calibrating on a Bluetooth speaker, your Perfect count on Warm Up goes up.

## Stretch

- A fourth song, or a song editor that reads the text format from a box on the page.
- Mirror mode (lanes flipped) and a random lane mode for replay value.
- Share a result card as an image with `canvas.toBlob`.
- A daily song pick seeded by the date, with a local streak.
- Gamepad buttons with the Gamepad API.

## Leave out

- Sound files, Tone.js or any audio library.
- A server, accounts, online leaderboards.
- A full chart editor.

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