Inspiration

Most calorie apps hand you a number and a food log, and leave you to work out the rest. Real life in San Francisco is harder than that. Rent is high, groceries are expensive, and plenty of people eat whatever is close and quick. In 2024, 28% of lower-income adults in San Francisco were food insecure, and 67% of people the SF-Marin Food Bank serves have jobs.

We wanted a food buddy that starts from your body and your block instead of a blank log. Tell it about yourself once, in your own language. It works out what you need, explains it in plain words, and plans meals from what's on sale near you this week. That buddy is Pip.

What it does

Pip is a small character in the corner of the app. Tap it and it explains what's on screen, one line at a time. You can rename it, change its color and hat, or upload your own picture.

Behind Pip is the planner:

  1. You tell it about yourself. Age, sex, height, weight, how you exercise, your goal, allergies, diet and health conditions. Body fat and muscle mass from a smart scale are optional. You can say it out loud ("I'm 29, 5'5" and 140 lb, I lift three times a week"), and Pip pulls the numbers out of the sentence.
  2. It works out what your body needs. Calories, protein, carbs, fat, fiber, water and nine micronutrients, each from a cited source.
  3. It explains it in five plain lines, in your language. For Valentina, our demo user, who is anemic and speaks Spanish:

Hierro: 18 mg al día. Come el hierro con vitamina C (morrón, naranja, frutillas). Deja el café y el té una hora lejos de esas comidas.

  1. It plans the day. Each meal is a top-rated recipe, sized to that meal's share of the day. When iron matters, it adds an iron food with a vitamin C food at lunch, so one helps absorb the other.
  2. It shops near you. The shopping list shows what's on sale this week at stores within your walking distance, with walk times. When money is tight, it points to CalFresh, and to farmers markets nearby that add Market Match dollars for produce.
  3. It learns. Star a recipe, or tap Like or Swap on a food, and the next plan changes. Pip remembers.
  4. It fits your week. Meal times go on your calendar (21 events for the week), and the grocery list goes to your phone as a text. Both wait for your tap.

How we built it

The math

Resting calories use the Mifflin–St Jeor equation, with weight $m$ in kg, height $h$ in cm and age $a$ in years:

$$\text{BMR} = 10m + 6.25h - 5a + s, \qquad s = \begin{cases} +5 & \text{male} \ -161 & \text{female} \end{cases}$$

When body fat is known, we switch to Katch–McArdle, which fits muscular people better:

$$\text{BMR} = 370 + 21.6 \cdot m(1 - f)$$

For Valentina (62 kg, 165 cm, 29, female):

$$\text{BMR} = 620 + 1031.25 - 145 - 161 \approx 1350$$

Lifting three times a week puts her activity factor at 1.55, so she burns about $1350 \times 1.55 \approx 2090$ calories a day. Losing roughly a pound a week means a 500-calorie deficit, which gives her 1,590 calories a day. Plans never go below a safe floor, 1,200 calories for women and 1,500 for men.

Protein is 0.8 g per kg for most adults, and 1.4 to 2.0 g per kg for people who train. She lifts and wants to lose fat, so she gets $1.9 \times 62 \approx 118$ g, spread across meals.

The recipes

There is one recipe per dish, ranked by a Bayesian average so a recipe with three 5-star reviews doesn't beat one with six hundred. Here $v$ is the recipe's review count, $R$ its rating, $C$ the mean rating, and $m = 20$:

$$\text{score} = \frac{vR + mC}{v + m}$$

Each recipe is scaled to its meal's share of the day: 25% breakfast, 35% lunch, 30% dinner and 10% snack. Star ratings feed Thompson sampling over cuisine and main ingredient. For each one, Pip draws

$$\theta \sim \text{Beta}(\alpha, \beta)$$

so it leans toward what you like and still tries something new now and then.

The groceries

Pip looks up your address in the City's address registry. It finds grocery stores and farmers markets around you on OpenStreetMap, within a walking radius of $r = 80 \cdot t$ meters for $t$ minutes. Then it searches this week's store flyers for your ZIP code, keeps the items that fit your diet and allergies, and matches each one to a store you can walk to.

The stack

  • Runtime: Node 24 runs TypeScript directly, SQLite is built in, and there's no build step.
  • Rules: every target and every health condition lives in a JSON rule file with a source URL and who to confirm with. Sources include the National Academies' Dietary Reference Intakes, the NIH Office of Dietary Supplements, the CDC, the NIDDK, ACOG and the American Heart Association.
  • Food data: nutrient values come from USDA FoodData Central, recipes from Food.com, and ingredients with quantities from RecipeNLG.
  • Languages: explanations are written in English, Spanish and Portuguese.
  • Safety: nothing is sent without your tap.

Challenges

Health safety came first, and our first version got it wrong. Our condition matcher read "second trimester" as kidney disease, because "rim" is Portuguese for kidney. That silently dropped protein targets for pregnant users until a review caught it. We also found that "nut allergy" still let almonds through. Those are fixed. Several conditions now change what Pip shows:

  • Kidney disease gets no protein or potassium target, because those depend on lab results.
  • An eating disorder history gets balanced plates with no calorie numbers anywhere.
  • Pregnancy never gets a weight-loss plan, and tuna is left out of daily meals.
  • Nobody under 18 gets a plan.

Matching flyers to real food is messy. A search for "eggs" returns egg noodles, and a search for "milk" returns almond milk. Every food needed its own words to match and words to exclude. A local shop called "Valencia Whole Foods" kept getting deals meant for the Whole Foods chain, until we matched on the brand or the start of the store name.

Protein kept coming up short. The first plans leaned on beans and potatoes and reached only 62% of the protein target. We started scoring foods by protein per calorie and trading starch for lean protein until the day reached about 90%.

Free map servers fail often. OpenStreetMap's servers sometimes time out and report it as a successful empty answer. We query three servers at once, treat empty answers as failures, and cache store lists for a week. If the map is down, Pip still shows the deals for your ZIP code without walk times.

Late features broke earlier ones. Recipes arrived late in the day and briefly cut the link between meals and nearby deals. Tests, plus a live check against real store and City data before every push, caught it.

What we learned

  • A number alone doesn't change what anyone eats. "Iron with vitamin C, coffee an hour later" does.
  • Calories, protein and fiber are easy to cover. Vitamin D and B12 are hard to get from food alone, so Pip says to ask a doctor rather than pretend a plan covers them.
  • "Information, not medical advice" only means something if the code enforces it. Every condition names who to confirm with, and some conditions turn features off entirely.

What's next

  • Recheck every nutrition source against its official page. All of them are still marked "not checked yet" in the app.
  • Let Pip compare stores across the whole week's plan, not just one day.
  • Add more languages for the explanations.

Built With

Share this project:

Updates

Submission history