Project · ★★ · Intermediate
Pulse
A rhythm game you would actually show a friend. Four glowing lanes, notes fall to a line, and you tap D F J K, or the lanes on your phone, as each one crosses. Hold the long ones. Perfect, Great or Miss, a combo that multiplies, a grade from D to S. The three songs are text in a file, played by the browser with no sound files, and the notes you play are built from the melody. The whole screen pulses on the beat, so you can play it on mute on the bus.
Play it The finished game, built from the brief below. Sound on is better, but it works without.
You need
- Set up your computer, so you have a terminal, git and Node.
- Claude Code or Codex, set up and logged in: Get Claude Code or Get Codex.
- How to work with an agent. Read it once. It saves hours.
- A free GitHub account, if you want to publish it: Save it on GitHub.
- Headphones help. Phone speakers lose the kick.
What you'll learn
- Timing on the audio clock: why a game that judges milliseconds cannot trust a normal timer.
- Data over files: a song written as text, and a chart built from it, instead of a sound file plus a hand-made note list.
- Canvas drawing with glow, particles and screen shake, in one draw loop.
- Designing visual cues that carry a game when there is no sound.
- Multi-touch with pointer events, so two fingers can hold two lanes.
What this shows the agent can do
- Compose a short tune in a text format you made up, then fix the one bar that sounds wrong.
- Turn a vague ask like "make it feel juicier" into sparks, rings, a jumping combo and a shake.
- Keep the rule that every time comes from the audio clock, across every later change.
The brief
Make a new folder for the project. Copy the brief below into a file called BRIEF.md in that folder, or download BRIEF.md. Open the agent in the folder and say:
Read BRIEF.md and start with milestone 1. Stop after each milestone so I can check it.
# 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.
Milestones
The same milestones as the brief. Tick each one off when its check passes. Do one per sitting if you like. The dot in the list marks the point where you have something that works on its own. Everything after it makes it remember things or work for more than one person. Stop at the dot and use what you built before you go on.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
Make it yours
Once it works, pick one of these and ask for it as a new milestone.
- 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.
If it goes wrong
Notes reach the line before or after the sound.
The drawing loop is using its own clock, or the start time was taken before the AudioContext was running. Say this to the agent:
The notes are out of time with the music. Compute song time as AudioContext.currentTime minus the start time everywhere, and take the start time only after the context is running.
Taps register as Miss on a phone even when they look right.
Phones and Bluetooth speakers add delay. Without an offset the judging is unfair. Say this to the agent:
My taps judge late on my phone. Add a Calibrate screen that flashes and clicks eight times, saves the average lateness, and subtracts it from every press.
Two fingers on two lanes only count as one.
The touch code tracks one pointer, or the canvas is not capturing each pointer id. Say this to the agent:
Two fingers at once only register one lane. Use pointer events, remember the lane per pointerId, and call setPointerCapture on pointerdown.