Vibecode build plans · № 6
Build a pomodoro timer
One file, one afternoon — a timer you'll actually trust, with the drift bugs headed off before they happen.
A pomodoro timer looks like the easy vibecode: count down from 25, ring a bell, count down from 5, repeat. It is a great first build precisely because it's small enough to finish today and useful enough to open tomorrow. The catch is that the "obvious" way to build it is subtly, quietly wrong — and you won't notice until you switch tabs to do the actual work.
This plan is opinionated about the one thing that matters here: time. A timer that loses a few seconds each cycle, or freezes the moment you background the tab, isn't a timer — it's a decoration. Most of this plan exists to steer your agent away from the naive countdown loop and toward a clock you can leave running while you work.
The spec — and the cuts
- One HTML file. Inline CSS and JS, no build step, no framework. A 25/5 work/break timer, a small task list, and a count of today's completed sessions. That's the whole app.
- localStorage, sparingly. At most, persist today's session count so it survives a refresh. Everything else can live in memory — this isn't a database.
- Cut: accounts and cloud sync. A pomodoro timer is a single-person, single-device tool. The moment you add login you've built a different, worse product.
- Cut: team features. No shared rooms, no "see who's focusing." That's a startup, not an afternoon.
- Cut: an analytics dashboard. Streaks, weekly charts, productivity scores — all of it is scope creep dressed as motivation. Today's count is the only number that earns its place.
The prompts
Tick the features you want — the prompts below rewrite themselves. Then paste them into Claude Code (or Cursor, or any agent) one at a time, in order.
Every toggle edits the prompts. The copy buttons grab the current version.
1 · Build it
2 · The accuracy pass (don't skip this one)
3 · Notifications & sound
4 · Get human eyes on it before you rely on it
Where agents go wrong on this build
- Decrementing a counter every 1000ms. The naive loop —
setIntervalticking asecondsLeft--— is wrong twice over:setIntervalis never exactly 1000ms, so the error accumulates over 25 minutes, and it stops advancing entirely when the tab is backgrounded. Compute remaining time from a target end-timestamp (endAt - Date.now()) and recompute on every tick, not from a running count. - Trusting the tab that isn't in front. Browsers throttle
background timers to save power, so an interval that "runs" every second may
fire once a minute. If you drive the display off tick count, it visibly
freezes and then jumps. Recompute remaining time from the timestamp on
visibilitychangeandfocus— treat the display as a view of the clock, never the clock itself. - Asking for permission on page load. Notification
permission prompts and audio
play()are both gated behind a user gesture. Fire them on page load and the browser silently blocks them — the first bell never rings and you can't tell why. Request permission and unlock the first sound inside a click handler (the Start button), never at startup. - Two timers racing after pause/resume. A common bug: pause
leaves the old interval alive, resume starts a second one, and now the
countdown ticks twice as fast. Keep a single source of truth for the
interval id, always
clearIntervalbefore starting a new one, and derive "running vs paused" from state — not from whether an interval happens to exist.
Why the feedback step is in the plan
You built this timer, so you know exactly which button starts it, what the bell means, and why the count resets at midnight. Nobody else does. Whether a stranger can tell at a glance that they're in a work block versus a break, whether the controls read as obvious or cryptic, whether the notification is a relief or a jump-scare — that's precisely the judgment you can't make about your own build. Prompt 4 wires your agent to our MCP server: it shares the live page, you send the link to a person or two, they tap and pin notes right on it (no login), and your agent reads the notes back and applies what survives your judgment. The loop that built this very page.