Focus Timer
What it does for you
A warm offline Pomodoro timer with a task log and ambient sound synthesized live in the browser, zero audio files.

Screenshots

In more detail
A focus timer that counts down work rounds and breaks, keeps a short task list, and plays background sounds like rain, a cafe, or a crackling fire. The sounds aren't recordings. The browser generates them live, so the whole thing is one file that works offline.
Honest limits
No automated tests. The drift-free timer is correct by design, but no test file checks it. The daily counts are stored but never shown as history. The synthesized sounds also still feel a bit dry and centered.
How I built it
Claude Code, about 2 hours. I set the concept (timer, task log, and ambient sound, as far as it could go inside a T0 box) and a warm ember look. Claude Code wrote the whole single-file app: the timer with focus, short break, and long break phases, a round counter that moves into a long break every few sessions, a progress ring, and a countdown in the browser tab title. Each finished focus round drops a tomato tally on the active task and into a daily total.
The six sound channels (rain, brown noise, cafe hum, ocean, fire crackle, wind chimes) are built with the Web Audio API out of filtered noise and short scheduled tones. Each one has its own level under a master volume.
A few things went sideways. The design hook flagged my font pair, Fraunces and Space Grotesk, as overused, the same reflex it had caught on #6. We swapped to Newsreader and Hanken Grotesk, which fit the mood better anyway. The first headless screenshot was an error page because a Windows file:/// path came out malformed, so we served the app with serve.mjs and captured it over http instead. For the cover, Claude wrote a throwaway page that seeds localStorage with tasks and a sound mix, then redirects to the app, and screenshotted that.
What I learned
- A timer should never count its own ticks. Store the end time and work out what's left from the clock every frame. That survives background tabs slowing down timers, where a tick-counting clock quietly runs slow.
- A soundscape can be synthesis, not audio files. Brown noise is a smoothed random walk, rain is filtered noise with a slow swell, and fire is a string of tiny random pops. Six channels, zero bytes of audio.
- Browsers won't start audio without a click or key press, so saved sound channels can't resume on load. Re-arming them on the first click is what makes "it remembers my mix" work.
- Screenshotting a seeded state documents the app and tests it at the same time. The cover image is also proof that tasks, tallies, theme, and sound levels all save.
What carried forward
It reused the serve.mjs idea from #7, on port 5178. The seeded-state screenshot solved the same problem the September build brief later handled with a ?demo=1 flag: getting a repeatable, filled-in screenshot every time. And the font hook kept doing its job on #9 and #10.
If I took it further
I'd add a simple daily and weekly history view, since the counts are already stored, and some stereo width and light reverb so the sounds feel less flat.
#008 Built with Claude Code June 23, 2026