Inspiration
Food labels are a mess — tiny print, ingredient names nobody teaches you, and allergen warnings buried in a wall of text. For teenagers especially (who are increasingly managing their own snacks, lunches, and eating out with friends), that's a real barrier to making informed choices, and a real risk if you have an allergy. We wanted something that could do in three seconds what currently takes squinting at a label and Googling five ingredient names: just tell me if I can eat this, in plain English.
The "Global & Local Impact" theme pushed us further — allergen labeling conventions, languages, and even which allergens are commonly declared vary wildly by country. A universal camera-in, plain-English-out interface felt like it could travel anywhere a phone does.
What it does
BiteCheck has three core flows:
- Scan a single item — take a photo of a label or ingredient list (or just type it in), and get an instant health score, nutrition breakdown, allergen flags, and a casual, judgment-free summary.
- Save an allergy profile — pick from the common "Big 9" allergens plus anything custom, once, on your device. Every scan after that automatically checks against your list and surfaces a loud, specific warning if something matches.
- Menu Check — photo or paste a whole restaurant menu, and every dish gets sorted into Avoid / Caution / Safe, with a plain-language reason, so you're not the person at the table squinting at descriptions trying to reverse-engineer whether the pad thai has peanuts in it.
How we built it
The stack is intentionally boring where it counts: a vanilla HTML/CSS/JS frontend (zero build step, so vercel deploy has nothing to compile), a thin Node.js backend, and a vision-capable LLM doing the actual reading and reasoning. We split the AI logic into two dedicated modules — one for single-item nutrition analysis, one for menu-wide allergy triage — each with its own carefully-shaped JSON schema so the frontend never has to parse free-form text.
A design decision we're proud of: the exact same core logic file is imported by both the local Express dev server and the Vercel serverless function. Local and production behavior literally cannot drift apart, because they're not two implementations — they're one function called from two entry points.
On the design side, we leaned into a "scanner" visual identity — viewfinder corner brackets, an animated scan-line sweep, mono-font nutrition numbers styled like an actual nutrition label — instead of a generic upload box, to make the whole thing feel purpose-built rather than templated.
Challenges we ran into
- Moving targets in the AI provider landscape. Mid-build, our chosen model got deprecated by the provider with only a few weeks' notice, and its replacement turned out to have a "thinking mode" that silently ate its entire token budget on internal reasoning before ever producing the JSON we asked for — resulting in an opaque validation failure with no visible cause. Disabling reasoning mode and surfacing the model's raw partial output in our error messages turned an untraceable bug into a two-minute fix.
- A CSS specificity trap that looked like a logic bug. Several UI elements have a hidden attribute and a class that sets display explicitly — and when both have equal CSS specificity, our own stylesheet was winning the tie against the browser's built-in [hidden] { display: none } rule. The result: a red allergy-warning banner that appeared to ignore its own hidden state. The fix was small (:not([hidden]) guards), but tracking down why a boolean flag wasn't controlling visibility took real debugging, not guessing.
- Personalization without over-promising. We wanted allergy matching to feel genuinely useful, not like a legal disclaimer generator. That meant tuning the prompt to hedge appropriately on ambiguous menu items ("Caution" rather than a false-confidence "Safe") while still being direct and fast to read — a balance that took several iterations of prompt wording.
What we learned
Building on a fast-moving LLM API taught us to design for change from day one — model names, capabilities, and quirks (like hidden reasoning modes) shift under you mid-project, so isolating all provider-specific logic into one swappable module wasn't just clean architecture, it saved us when we had to pivot providers entirely partway through the build. We also relearned a classic lesson the hard way: the bugs that look like backend logic errors are sometimes just cascade order in a 500-line stylesheet.
Log in or sign up for Devpost to join the conversation.