CaloVoice: Voice-First Calorie Tracking
Designed and built a voice-powered calorie tracker that turns natural speech into structured food logs using an LLM pipeline, reducing meal logging from ~2 minutes to under 10 seconds.
Problem
Calorie tracking has a compliance problem, not a feature problem. Apps like HealthifyMe and MyFitnessPal require ~2 minutes per meal (searching databases, scrolling variants, adjusting servings), 3–4× a day. Most users quit within two weeks. The friction of logging outweighs the perceived value.
People describe meals in natural language effortlessly. "Two rotis with dal and a bowl of curd" takes 3 seconds to say but 2 minutes to manually search and log. Voice isn't just a convenience. It's the difference between a habit that sticks and one that doesn't.
Solution
Voice-first logging: tap, speak, done. An LLM pipeline (Web Speech API → OpenRouter → CalorieNinjas) parses natural speech into structured food entries with calorie estimates in under 10 seconds.
Silence-as-submit: no 'Done' button during recording. 3.5s of silence after speech auto-submits. Silence is the punctuation.
Graceful degradation: if voice fails (unsupported browser, API error, empty parse), the user lands on a manual form silently, never an error dead-end.
Time-aware UI: the CTA adapts ('Log Breakfast' → 'Log Dinner'), and a pace tick on the calorie ring shows where you 'should' be at this hour.
Predictive coaching: after 2+ meals, the app projects end-of-day intake, 'At your current pace, you'll exceed by ~400 kcal. Consider a lighter dinner.'
Voice Pipeline
Key Decisions
- PWA over native: MVP speed. Validates the voice interaction model before investing in native. V2 roadmap includes native for widget-based logging without opening the app.
- No global state store: Firebase real-time listeners ARE the state layer. Components subscribe via hooks. No Redux, no Zustand, zero redundant state.
- LLM via OpenRouter (model-agnostic): can switch between GPT-4, Claude, Llama without code changes. JSON mode ensures structured output. Local food dictionary reduces API calls for common items.
- All CSS animations, no libraries: pure CSS with spring-eased curves. Sheets animate out before unmounting (240ms exit). Zero JS animation runtime cost.
Design System Enforcement
Design token violations are a build error, not a code review comment. Two custom ESLint rules make it impossible to ship raw values to production.
Vision & Roadmap
- V1 (current): Voice-first logging, calorie ring with pace indicator, contextual insights, activity tracking.
- V2: Conversational AI coach. Analyzes your day's data and guides real-time decisions ('You have 350 kcal left, skip the chips, try almonds with dark chocolate').
- V3: Native app with home-screen widget. Tap mic, speak, done. No app open required. Background LLM processing.
Success Metrics
- Voice vs. manual usage ratio
- Time-to-log (target: under 10s for voice path)
- LLM parse accuracy (edit rate before save)
- 7-day and 14-day retention
- Meals logged per day consistency
Tech Stack
React 19 · TypeScript · Firebase (Auth + Firestore) · OpenRouter (LLM) · CalorieNinjas API · Web Speech API · Vite · MUI · Zod · PWA
Voice logging works not because it's faster. It's faster because it eliminates the translation step. Users already narrate meals in natural language; the old apps forced them to translate that narration into a database search. Removing the translation is what closes the habit gap, not the speed improvement itself.