Rasveda Labs Case study · Portfolio

Ingredient.help

Scan · Score · Verdict

Scan any product. Understand what you’re really buying.

Point a camera at a barcode or an ingredient list — or just type a brand — and get back a score, a verdict, and the reason behind it. Food, cosmetics and pet food, each read through its own lens. The target is twenty seconds from scan to decision.

Role
Product · Content · Full-stack engineering
Stack
Next.js 14 · React 18 · TypeScript · Supabase · Claude
Status
Working product · catalog still small
The idea

The scanner is the product.

An ingredient list is a legal document printed in six-point type, written for a regulator and handed to a shopper who has thirty seconds and no chemistry degree. This project began as an encyclopedia of additives with a Laboratory for mixing them — and both still work — but they were the wrong front door. Somebody standing in an aisle holding a tin of cat food had no idea what the site was for.

The identity is now singular. The scanner is the way in. The ingredient database is the engine underneath it — 325 pages exist so that a scan can mean something, not so people can browse them. And the generated report is a recommendation about this product, never an encyclopedia entry: standing facts about an ingredient belong on that ingredient’s page, and the report is forbidden from repeating them.

The founding principle

No data never means “safe.” Where the evidence is missing, the app says no known data — out loud, and never anything more comforting.

The answer

Twenty seconds, in a fixed order.

A reader is assumed to have twenty seconds, not five minutes. Every screen answers questions in the same order, so stopping early still leaves you with an answer rather than a pile of facts.

🐾 Read as pet food · auto-detected · cat
78/100 Good

A solid everyday wet food for an adult cat, held back by one vague protein line.

Why — four points, one line each
  • StrongestNamed muscle meat leads the list, before any water or broth.
  • Weakness“Meat and animal derivatives” hides which species it came from.
  • SafetyNo pet hazards found; no onion, garlic or propylene glycol.
  • Bottom lineGood enough to feed daily; better options exist at the same price.
The working — score breakdown, dry-matter figures, every matched ingredient, the tokens we couldn’t place

An illustration of the shape, not a real product’s result. The score is arithmetic: a model rates five observable factors 0–10 at temperature 0, and fixed weights turn those into the number. It is never a figure the model picks.

Three lenses

The same label asks three different questions.

The app reads the label and decides which question it is being asked, then says what it decided — with a one-tap override, because a wrong lens makes the whole verdict wrong.

01

Human food

The allergen verdict: the label read against your own profile, stored on your device and never sent anywhere. 19 allergens, with the sensitivity flags that change what counts.

02

Cosmetics (INCI)

377 INCI names recognised and grouped by function, across 20 concern classes — parabens, sulfates, fragrance allergens, formaldehyde releasers, UV filters. Framed as worth knowing, never as unsafe.

03

Pet food

10 pet hazards, each naming the species affected. Two further facts are established first — cat or dog, and dry or wet — each needing two independent confirmations before it is trusted.

04

Why the lens decides everything

A cat is an obligate carnivore and a dog is not, so the same recipe earns different verdicts. Form matters too: wet food lists water by weight, kibble structurally needs binder starch. Guess either wrong and you get confident nonsense.

Getting in

Three entrances, one pipeline.

Barcode, photo, or a typed brand name. A barcode is a shortcut to the ingredient text, never a different code path — searching a brand simply resolves to a barcode and hands over. That is why three entrances can’t drift into three different answers about one product.

A

Every browser is covered

The native barcode detector where it exists, ZXing everywhere else — all iOS browsers are WebKit, so even iOS Chrome needs it — and a keypad over the live preview for a code that will never decode: scratched, curved, shrink-wrapped.

B

Our own camera, not the phone’s

The OS camera photographs a whole packet with the list as a small block inside it. Here the list is framed before the shutter, then cropped and compressed on the device — the upload is the list at 1600px, not the bag at 4000px.

C

A bad read never becomes data

Every read is scored for quality. A poor one is declared before any verdict is drawn from it, is never auto-saved to the shared catalog and never keys a shared report — so one blurry photo can’t poison a product for everybody else.

D

The fallback is the edge

Three tiers of trust — our verified catalog, community readings, then the open databases — and a write never buries a better one. When nobody knows the code, the label gets read anyway. Barcode-only apps go blank exactly when a shopper needs an answer most.

The sharpest wedge

A tin at 11% protein beats a bag at 30%.

Pet labels print a “Guaranteed Analysis” that is not comparable between products: four fifths of the tin is water, and the percentages are of the pack. Nobody does that arithmetic in an aisle, and almost nobody knows it needs doing.

So the Laboratory carries a second bench that puts up to four pet foods on one scale. When the pack that looks highest in protein isn’t the one that is, the screen says so in a sentence — that is a fact about the arithmetic, not an opinion about the food. It also offers grams per 100 kcal, because an animal eats to its calorie need, and carbohydrate by difference, which no pet food label prints anywhere.

Every figure is divided out of numbers printed on the pack. No model is asked, nothing missing is estimated, and any of it can be checked by hand in front of the tin. A pack that doesn’t print its moisture is left off the scale rather than given a typical value for its form. Nothing is ranked: more protein is not better for a cat in renal failure.

The Laboratory & the diary

What a single label can never tell you.

A report reads one label. It can say a drink carries two sweeteners; it cannot say that the benzoate in the drink met the ascorbic acid in the salami four hours earlier. That pairing exists only in the day — so the diary looks for it, and the Laboratory explains it. 214 documented rules, engine v1.17.0, on a four-step meter:

Sodium benzoate E211 Ascorbic acid E300 · vitamin C
Can form benzene High

Together, and especially with heat or light, benzoate and vitamin C can react to form benzene. Condition-gated, but flagged even when conditions are unstated — at reduced confidence. The highest matched pair wins; a real risk is never averaged into a comfortable middle.

Tea polyphenols Porridge non-haem iron
Lowers iron absorption from that meal Low

Eight everyday foods are in the catalog — not because they’re common, but because each takes part in a documented interaction. And the claims stay the size of the evidence: tea with a meal measurably lowers that meal’s iron, and does not give anyone anaemia. A test fails if any food rule is filed above low risk.

Under the hood

If it can be counted, it is counted.

The rule everywhere in this codebase. The score is arithmetic. The diary is arithmetic. The comparison chart is arithmetic. Interaction findings come from a rules engine, not a prompt. A model gets exactly two jobs: reading text off a photograph, and writing prose about facts established before it was called.

A

The report is validated before you see it

Structural faults are removed — an audience group about the wrong animal, a marketing claim with no claim in it. Prose faults earn exactly one corrective retry: discussing the wrong species, or a verdict whose confidence contradicts its own score.

B

Cached by product, not by user

Four cache layers keyed by barcode, ingredient fingerprint or pair. The first reader of a product pays; everyone after them reads it instantly for nothing. The bill scales with the catalog, not with traffic.

C

Immutable, versioned snapshots

Every saved analysis stamps its engineVersion and rulesVersion and freezes what it used, so an old result stays explainable after the rulebook moves on.

D

It boots with nothing configured

No API keys at all and the catalog, ingredient pages, Laboratory and on-device OCR still work. Each key adds a capability; its absence is handled explicitly rather than as a crash.

E

Privacy is structural

The diary, the allergen profile and the lab bench live in the browser and never leave it. Scanning has never required an account. The coverage corpus that tells us what to add next carries no image and no identity.

F

Two people, one URL

A shopper gets an answer; an operator gets catalog tooling. The split is presentational and says so in the code — every write to the shared database is checked again server-side, where a hidden button is no defence.

Counted, not claimed

The data assets.

Every figure below is counted from the repository as it stands today.

325
ingredient pages — 317 E-numbered, 8 base foods
214
documented interaction rules (engine v1.17.0)
800+
everyday foods recognised and grouped
377
INCI cosmetic names, in 12 functional groups
19 · 10 · 20
allergens · pet hazards · cosmetic concern classes
1,115
automated tests across 94 files
Design language

Soft laboratory.

Warm paper, ink-green text and a calm sage-and-mint accent — clinical enough to feel trustworthy, soft enough to feel human. Headings in Fraunces, body in Inter. The risk scale is a fixed, colour-blind-considered ramp from sage to clay so a verdict reads the same way every time — and the verdict panel takes the rating label, not the score, so it can never re-band at its own thresholds.

Paper · #F7F5EE Sage · #5C8E54 Mint · #A8D5BA Ink · #1F2A24
  • Fraunces + Inter
  • Paper & ink-green
  • Sage / mint accent
  • Fixed risk ramp
  • Hand-drawn beaker mark
The build

Stack.

Next.js 14 (App Router · React 18) with TypeScript and Tailwind, on Supabase with row-level security on every shared table — all of it written server-side with a service key, so nothing shared is reachable from a browser. Label reading runs on Google Vision or on-device Tesseract; barcodes on the native detector or ZXing. Claude writes the report and the comparison, on Haiku by default, with the model name deciding whether a thinking block and structured outputs are used at all. Vitest covers the deterministic core, and CI runs typecheck, tests and build on every push.

Next.js 14React 18TypeScript Tailwind CSSSupabase (RLS) Claude (Anthropic SDK)Google Vision Tesseract.jsZXingZustand Fuse.jsVitest
Status
Working product · catalog still small

Everything above is shipped. Here’s what isn’t.

Three scan routes, three lenses, the report, comparison, the dry-matter shelf, the ingredient database, the Laboratory, the diary with period recaps, product pages, discovery and confirmation, the contribution ledger and the admin tooling all work today, alongside a separate capture app that fills the verified catalog. What is not done — and isn’t pretended to be:

  • Billing isn’t wired. The credit ledger is complete and the price is chosen; no payment provider is integrated.
  • The verified catalog is small. It grows by walking shops with the capture tool — labour, not engineering. This is the main gap between the product and its own promise.
  • No mobile app. It is an installable, offline-capable PWA; the app stores are a distribution channel not yet used.
  • No marketing has been done, so there is no user base to report.

Choose with knowledge, not guesswork.

Ingredient.help is live — scan a pack today.