Inspiration
Many plant owners face the same problem — you buy a plant with good intentions, but over time, without the right care or attention, it starts weathering. Leaves yellow, growth slows, disease sets in — and the owner often has no idea why, or what to do about it. There's no one to consult in that moment. We have doctors for humans when something goes wrong with our health, but plants — which are living beings too — don't get that same safety net. Leafora exists to close that gap: to be the plant doctor people can turn to the moment something looks wrong, so a lack of knowledge doesn't have to mean a lost plant.
What it does
Leafora is a mobile-style web app where you photograph a struggling houseplant and get back an AI-generated diagnosis: plant name, overall diagnosis, a severity rating (low/medium/high), a breakdown of detected diseases each with its own prevention and treatment steps, and structured care guidance for watering, sunlight, humidity, and temperature. Beyond a single diagnosis, the app is built around five sections — Home, Plants, Diagnose, Care, Profile/History — so a user's plant collection and past diagnoses live in one place rather than being a one-off tool.
How we built it
Frontend: a single HTML file with vanilla JS and CSS, no framework — five "pages" implemented as divs toggled via showPage(), giving app-like navigation with a bottom nav bar, all in one static file AI: Google's Gemini API (gemini-1.5-flash), called with the uploaded photo (converted to base64) plus a prompt asking for a strict JSON response — plant name, diagnosis, severity, disease list, and care instructions Parsing: since Gemini's text response isn't guaranteed to be clean JSON, I extract it with a regex match for the JSON block before parsing Auth/key handling: users paste their own Gemini API key into a welcome modal (with a link to get a free key from AI Studio) on first load; the key is stored in the browser so they don't need to re-enter it each session Resilience: if the Gemini call fails for any reason, the app doesn't show a dead end — it falls back to a generic but still useful diagnosis (get Fallback Result()) so the results screen is never empty, even live on stage Backend (in progress): I also wrote a serverless function (api/diagnose.js, for Vercel) that runs the same Gemini call server-side using an environment variable instead of a user-supplied key — a step toward not exposing API keys in the browser at all Deployment: shipped to Vercel
Challenges we ran into
Getting reliable structured output from an LLM — Gemini doesn't always return pure JSON, so I had to regex out the JSON block from its response and design around the possibility of a malformed or missing match Client-side API keys are a real security tradeoff — storing the user's Gemini key in the browser was the fastest way to get a working demo without a backend, but it also means the key is exposed in the client; building the serverless route was my attempt to start moving away from that, though I haven't fully switched the deployed app over to it yet Designing for failure, not just success — early on, a failed API call meant a broken or empty results screen; I had to explicitly build a fallback diagnosis path so the app degrades gracefully instead of dying mid-demo Balancing a polished UI with a rushed timeline — some of the home screen (stats, sample history entries) is hardcoded demo data rather than live-wired to real diagnosis results yet, which meant making judgment calls about what to fake for the demo versus what had to be fully real Rendering AI-generated and user content safely — diagnosis text, disease names, and care instructions get injected via innerHTML; I had to be conscious that this content should really be sanitized/escaped before rendering, since it's untrusted text coming back from an external API
Accomplishments that we're proud of
Shipped a working end-to-end AI diagnosis flow — from photo upload to a real Gemini API call to a parsed, structured result rendered on screen, not just a mockup. A user can genuinely upload a plant photo and get back an AI-generated diagnosis with treatment and prevention steps. Built a UI that feels like a real app, not a hackathon prototype — five distinct sections (Home, Plants, Diagnose, Care, Profile/History) with smooth page transitions, a bottom nav bar, glass-morphism styling, and mobile-first responsive design, all in a single static file with no framework overhead. Designed for failure, not just the happy path — building a fallback diagnosis for when the API call fails was a small decision that mattered a lot: it means the app never shows a broken or empty screen, even under a flaky connection or bad key, live in front of judges. Started solving the API-key security problem, not just shipping around it — recognizing that a client-side key isn't the right long-term answer, and actually writing a serverless route (api/diagnose.js) to move the Gemini call server-side, rather than treating "it works" as good enough. Took the idea from a real, personal frustration to a deployed product — going from "plants deserve doctors too" to a live app on Vercel that anyone can open and use within a hackathon timeframe is something worth being proud of on its own. Handled the practical mess of working with an LLM's output — parsing semi-structured JSON out of raw model text, defending against malformed responses, and still keeping the UI clean and consistent regardless of what Gemini actually returns.
What we learned
Prompting an LLM for structured JSON is only half the job — the parsing and fallback logic around it is what actually determines whether a demo survives being shown live Client-side-only apps are fast to ship but you inherit real constraints — API key exposure, no persistence across devices — that a proper backend would normally absorb; the serverless function was my first step toward addressing that A good hackathon build should fail gracefully, not silently — the fallback-result pattern taught me that visible, useful degradation beats a blank screen or a console error every time Solving a problem for something as unglamorous as houseplants still counts — the best hackathon ideas often come from a small, real, everyday frustration rather than a big abstract one There's a real gap between "demo-ready" and "production-ready" (hardcoded stats vs. live data, client-side keys vs. server-side), and being honest about which parts are which — to myself and to judges — is part of doing this well
What's next for Leafora
Move fully to the serverless API route Wire real data into the home screen Add users account Sanitize AI generated content before rendering Track plant health overtime Push notifications and care reminders Broaden diagnosis accuracy
Built With
- css3
- gemini-1.5-flash
- google-ai-studio
- google-gemini-api
- html5
- javascript
- vercel
- vercel-serverless-functions
Log in or sign up for Devpost to join the conversation.