A personal food and workout tracker. Take a photo of a meal, get an itemised calorie and macro estimate from Claude, correct it if needed, and save it against a daily calorie limit. Log workouts and weight alongside.
npm install
cp .env.example .env.local # add your Anthropic API key
npm run devOpen http://localhost:3000. On a phone, add it to the home screen for an app-like experience.
The photo analysis runs in a server route (app/api/analyze/route.ts) so the API key never reaches the browser. That means the app needs a Node host, not static hosting. Vercel is the simplest option:
- Import the repo on Vercel.
- Add
ANTHROPIC_API_KEYandAPP_PASSWORDas environment variables. - Deploy, open the site on your phone, and enter the same password under Settings → App password.
The analysis route refuses requests without the password and caps each IP address at 40 analyses an hour (ANALYZE_RATE_LIMIT to change). Without that, anyone who found the URL could spend your Anthropic credits.
Photo analysis uses Claude Sonnet 5 by default. A typical meal photo plus the itemised answer is roughly 3,000 to 4,000 tokens, which works out to around one to two US cents per meal. Set ANALYSIS_MODEL to change it:
| Model | Env value | Rough cost per meal |
|---|---|---|
| Haiku 4.5 | claude-haiku-4-5 |
under 1 cent |
| Sonnet 5 (default) | claude-sonnet-5 |
1 to 2 cents |
| Opus 5 | claude-opus-5 |
3 to 5 cents |
Meals, workouts, weights, and settings live in the browser's localStorage on the device you use. Use Settings → Export backup to save a JSON copy, and Import backup to restore it on another device. Imports are validated against the app's schema before anything is overwritten, and a save that fails because the device is out of storage is reported instead of silently lost.
A service worker caches pages and build assets as you use them, so the app opens offline on any device that has visited it while online. Photo analysis needs a connection.
npm run dev– local dev servernpm run build– production buildnpm run typecheck– TypeScript checknpm run lint– ESLint