Inspiration
A smallholder farmer who spots strange spots on a tomato leaf has two options: guess, or wait for an extension officer who may never come. By the time the guess is wrong, a late blight outbreak can take a field in under a week. Meanwhile almost every farmer in that position is carrying a phone with a camera in it. AgroLens exists to close that gap. The diagnosis should take one photo, not one appointment.
What it does
AgroLens is a mobile-first plant health scanner.
Scan: take a photo of the affected leaf with the camera, or pick one from the gallery. Diagnose: the image is classified and returned with a confidence band (low/medium/high) so the farmer knows how much to trust it, not just a bare label. Act: every diagnosis maps to a plain-language explanation and 2–3 concrete actions, split into cultural, organic, and chemical options, each with an icon so the card reads even at low literacy. For fall armyworm, that is "pick the caterpillars by hand at dawn", then neem or Bt, then a named pesticide with a pre-harvest interval in that order, cheapest and safest first. Remember: scans are stored on the device, so history stays browsable with no account, no sign-up, and no signal. It covers tomato late blight, fall armyworm, cassava mosaic virus, coffee leaf rust, nitrogen deficiency, and healthy plants, and reports unknown rather than inventing an answer when the model is not confident.
How I built it
Client: React 18 + Vite + Tailwind CSS. Everything is constrained to a phone-width column with large touch targets and high-contrast colour, because it is used standing in a field in direct sunlight, one-handed. API: Node.js + Express. POST /api/analyze takes a multipart image, classifies it, joins the label to the advice catalog, and returns one self-contained scan result. Vision layer: a single pluggable module, classifyImage({ buffer, mimeType }), returning { label, confidence, alternatives, source }. Set VISION_API_URL, and it calls a hosted model; leave it unset and a deterministic mock runs. Everything downstream keys off label, so swapping the model touches exactly one file. Advice catalog: the agronomy is data, not code, a label-to-treatment map that a domain expert can extend without reading any React. Testing: Playwright end-to-end coverage of the full capture → diagnosis → history flow at a mobile viewport. Challenges we ran into Bandwidth. A modern phone photo is several megabytes, and rural data is metered and slow. The client downscales to 1024px and re-encodes to JPEG in-browser before upload, and stores a separate 320px thumbnail for history. localStorage is a 5 MB cliff, not a database. Naively saving thumbnails filled the quota and then threw on write, losing the scan the farmer had just taken. History became a circular buffer that sheds the oldest image data on pressure, so a save never fails. Provider response shapes all differ. Hosted vision endpoints return {predictions: []}, a bare array, or a single {label, score}. A normalisation step collapses all of them into one sorted list, so the app is not married to a vendor. Confidence is a UX problem, not a number. 0.71 means nothing to a farmer, and a raw percentage invites false trust. It is presented as a three-step band with the alternative diagnoses shown underneath. Advice has to be safe. Chemical recommendations always carry a dose-and-label warning and a pre-harvest interval, and always sit below the cultural and organic options.
What I learned
The hard part of an "AI for agriculture" product is not the model. It is everything wrapped around it: how uncertainty is shown, whether the recommended action is affordable, and whether the app still works when the network does not. Designing the model as a swappable boundary from day one meant the entire product could be built, tested, and demoed end-to-end without the model being finalised.
What's next for AgroLens
Swap the placeholder classifier for a fine-tuned crop-disease model and report per-crop accuracy. A true offline PWA: queue scans taken with no signal and sync when a bar of service appears. Local-language advice (Amharic first) and audio playback of each action. Geotagged, anonymised scans to surface outbreak clusters for extension services.
Built With
- canvas
- computer-vision
- express.js
- javascript
- localstorage
- mobile-first
- multer
- node.js
- playwright
- react
- rest-api
- tailwindcss
- vite
Log in or sign up for Devpost to join the conversation.