Method
The common criticism of scanner apps is that they hand down a number and compress the reasoning to a summary. This page is the reasoning. Every rule below is implemented in lib/score.ts, and any product page shows the same arithmetic applied to that product.
The shape of the score
Four axes, each scored 0–100 on its own terms, then combined at weights you choose. Nothing is hidden in a constant.
| Axis | Spoil | Conventional |
|---|---|---|
| Nutrition | 55 | 60 |
| Additives | 25 | 30 |
| Processing | 20 | 0 |
| Organic label | 0 | 10 |
Two deliberate departures. Processing gets its own axis because the NOVA group arrives free with the data and answers a question a composite hides. The organic bonus starts at zero because certification describes how a food was farmed, not what is in it, and dietitians consistently flag it as the weakest leg of this kind of algorithm. Both are sliders.
If a weighted axis has no data for a product, its weight is redistributed across the axes that do, rather than counted as a zero. Missing data is not the same as bad data, and the product page says when this happened.
Nutrition
Open Food Facts computes the 2023 Nutri-Score, which totals negative points for energy, sugars, saturated fat and salt, and positive points for fibre, protein and fruit, vegetable and legume content. The final points are negative minus positive, so a lower number is a better food.
Those points map onto grade bands. Spoil gives each band a 20-point slice of 0–100 and interpolates inside it, so the letter and the number can never disagree: a grade A product cannot score 61, and a grade E product cannot score 21. Solid foods use the bands A ≤ 0, B 1–2, C 3–10, D 11–18, E ≥ 19; drinks use their own set, because the algorithm scores them separately.
The 2023 algorithm also drops the protein credit once negative points reach 11, which stops a high-sugar or high-fat product buying its points back with protein. The product page prints this when it fires.
Additives
Every declared E-number is matched against a register of 116 entries, each carrying a published EFSA, IARC, FDA or EU position and the source it came from. The axis starts at 100 and each additive subtracts: 25 for moderate, 10 for limited, nothing for low or unrated.
A high-risk additive zeroes the axis and caps the whole product at 49 out of 100 regardless of how it scores elsewhere. That mirrors what conventional scanners do and it is defensible, but it overrides everything else, so it is a toggle. When it binds, the product page prints the uncapped weighted total next to the capped one.
Each penalty is then scaled by how much of the additive can be in the product. This is the one place Spoil departs hardest from the rest of the category, which scores an additive on presence alone. Presence is not a dose, and the World Cancer Research Fund made exactly this criticism of the app that popularised the format.
A label bounds how much can be there. It never states how much is. So the number Spoil works from is a ceiling and is always written as one. Four things can bound it: a declared percentage, a “contains less than 2% of” clause, the rule that both EU and US lists run in descending order of weight, and the simple fact that a part cannot exceed the whole it sits in. The tightest of those wins, and the product page says which one it was.
Open Food Facts publishes its own per-ingredient estimate. Spoil does not use it and will not. It halves the remaining mass at each step down the list, so the figure compounds into nonsense: Yellow 6 in a bag of Doritos comes back as 0.0000924% of the product. On another record the estimate exceeds the maximum it sits under. A wrong number carries further than a missing one.
| At most | Penalty kept |
|---|---|
| 0.1% of the product | 15% |
| 1% | 25% |
| 2% | 35% |
| 5% | 60% |
| More, or nothing bounds it | 100% |
The bands are coarse deliberately. A ceiling is not a measurement, and a smooth curve drawn over it would imply a precision the label does not carry.
Three entries are exempt, and the test for exemption is narrow: the responsible agency set no safe level at all. Titanium dioxide is the clear case. EFSA concluded in 2021 that genotoxicity could not be ruled out and therefore published no acceptable daily intake, so there is no threshold for a ceiling to argue against and no amount of it earns a discount. Red 2G and potassium bromate are exempt for the same reason.
Everything else is scaled, including the additives carrying an IARC classification. IARC classifies hazard, not risk, and the agencies maintain an acceptable daily intake alongside those listings: JECFA reaffirmed aspartame’s 40 mg/kg in the same 2023 assessment that produced the Group 2B entry. Refusing to scale those would contradict the sources this register cites.
The 49-point cap is untouched by any of this. High risk in this register means an agency withdrew safety, classified the substance, or mandated a warning label, and all three attach to presence rather than to quantity.
The register states findings and attributes them. It does not editorialise about named products, which is both better design and the safer posture: the French charcuterie federation sued a rival scanner app over exactly this line, and although that app won on every appeal through the Paris Court of Appeal in June 2023, it cost years.
The ingredient list
The list is what people actually read on a pack, so every product page walks it line by line: what each ingredient is, the published position on it, and who published that. Ingredients are printed in descending order of quantity, which is the most useful fact on a label and the one a block of text buries, so the order is kept and the percentages are shown beside it.
E-numbers route to the additive register. Everything else is matched against a second register covering free sugars, refined starches, whole grains, saturated and unsaturated fats, partially hydrogenated oils, salt, nuts, legumes and produce, each with the WHO or EU position it is scored on. An ingredient outside both registers is marked not rated, which is a gap in the register rather than a clean bill of health.
A percentage marked est. is Open Food Facts’ estimate derived from the order and whatever figures the label declares, not a number off the pack. Ingredient notes are shown alongside the score but do not feed into it: the score comes from Nutri-Score, the additives and NOVA, as set out above.
Processing
The NOVA group, 1 to 4, mapped to 100, 75, 50 and 0. Group 4 is an ultra-processed formulation typically containing ingredients not used in home cooking. Many people care more about that than about any composite number, and most apps bury it.
Your profile never moves the number
Allergens, dietary requirements and hard limits on salt, sugars or saturated fat produce printed statements of what the product record declares, checked against settings you chose. They do not adjust the score and they are not advice. The number stays comparable between products; the judgement stays yours.
Nothing in your profile leaves the device. There is no account and no server-side record of what you look up.
Category context
A red score on olive oil or nuts is a known Nutri-Score artefact. Every product page also ranks the product against the popular products in its own category, so you can see both answers: how it scores against all food, and whether it is a good example of what it is.
What this cannot tell you
Coverage is uneven. Open Food Facts is strongest in France and the EU and noticeably thinner for products sold only in the United States, and a product with an incomplete ingredient list will lose whole axes.
Transcription varies more than the food does. A search for one sports drink returns records that agree on the Nutri-Score grade and the NOVA group but list anywhere from one to nine additives, because different people typed in different amounts of the same label. Those records score tens of points apart while describing nearly the same product. Spoil shows the additive count on every row so the cause is visible rather than mysterious, but it cannot invent the missing lines. The fix is someone completing the record.
A list will not show a score at all unless a Nutri-Score exists for the product. The remaining axes would still produce a number, but it would mean something different from the numbers around it, and a row has no room to explain that. A product page can rescale the weights and say so; a list declines instead.
The register is a reading of the published literature, not a substitute for it, which is why every entry names its source. Portion size, frequency and the rest of your diet are not modelled at all. And nothing here is medical advice.