DEV Community

Cover image for Treads - Stop Looking, Start Noticing
Ayush
Ayush

Posted on

Treads - Stop Looking, Start Noticing

Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass Submission 🌿

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass

Step outside. Notice what lives.

What I Built

Bored of your evening strolls, starting to lose interest? Maybe I have something for you to do while get your daily steps in!

Treads is a field journal that is a memory of what you experienced during your walk. Saw a cute puppy? Click a pic and it to your journal! Saw a plant and curious? Click a pic to get to learn what it is and learn some quick facts about it! Had a thought that needs to jotted down? Even better , just voice record it, and you can also generate a transcript!

You start a walk, spot a leaf or a bird or a beetle, and Treads helps you work out what it is, then sends your eyes back to the real thing. The screen is the notebook. The walk is the point.

The loop:

  1. Start a walk. Pick a 1, 2 or 3 km loop (roughly 15, 30 or 45 minutes) or just wander (free walk). GPS breadcrumbs track the route.
  2. Two taps to identify. Photograph what you found and pick from up to three candidate species, each with an honest confidence label: Likely, Possible or Unsure.
  3. Read the field card. Three short facts, each tied to a sentence in the species' Wikipedia article, plus a Look closely prompt: a single line that points you at leaf veins, bark ridges or wing borders on the thing in front of you.
  4. Say what you noticed. Hold a button and talk. Your voice note is transcribed and saved, and fact cards can be read aloud so your eyes stay up.
  5. Finish the walk. Gemma writes a short field dispatch about the walk and notes which of the species you met are ecologically connected.
  6. Come back tomorrow. A daily hunt gives you a small target ("two trees and a bird"), and your streak grows a flower garden.
  7. Keep it. One tap exports everything as an Obsidian-compatible vault.

It is for walkers, students, and anyone who has ever scrolled past a tree without knowing what it was.

Treads follows one rule, and it shaped most of the code:

The model suggests. Wikipedia verifies. You decide.

It also refuses when it shouldn't guess. Point it at a coffee mug or a person and it says so instead of inventing a species.

Landing Page

Demo

Live app: treadss.vercel.app

Open it on a phone, because camera and GPS are the whole experience. The walkthrough:

  • Accept the privacy gate (it explains what stays on your device and what doesn't).
  • Tap Begin a walk and choose a loop.
  • Photograph a plant, insect or bird and pick a candidate.
  • Read the field card, then tap the speaker icon to hear it.
  • Check your Life list and Journal, then watch the garden grow.

Flower-Garden when having a regular walking streak

Walking Interface

Walk Summary Example

The demo endpoints have daily quotas (for example, 30 identifications and 60 fact lookups per day) so a burst of visitors cannot drain the API budget.

Code

Treads Main

Treads 🌿 — Hacktoberfest Touch Grass

Step outside. Notice what lives.

Treads is a mindful, local-first naturalist companion designed to encourage outdoor observation and mindful walking. Built for Hacktoberfest Touch Grass, it turns routine neighborhood walks into observational field journals by weaving together on-device sensory experiences, pedestrian loop generation, open-weight machine intelligence, voice-recorded reflections, and seamless Obsidian vault exports.


🍃 Core Philosophy: Mindful, Grounded, and Local-First

Unlike conventional nature apps that require accounts, monetize social streams, and store location histories on remote servers, Treads is built around quiet craftsmanship and personal sovereignty:

  • 100% Local-First Data Sovereignty: Your field journal entries, specimen photographs, voice recordings, and GPS coordinates live entirely in your browser's IndexedDB (Dexie). No accounts, zero analytics trackers, and zero server-side persistence.
  • Client-Side Privacy & EXIF Stripping: Captured specimens are rasterized directly onto an HTML5 Canvas to strip 100% of hardware and GPS metadata before any transient AI…

MIT licensed. The repository was started on October 8, 2026

How I Built It

Treads is a Next.js 16 / React 19 app. The stack, in the order a photo travels through it:

  • Dexie (IndexedDB) holds every observation, GPS breadcrumb, photo and voice note on the device.
  • Gemma 4 (gemma-4-26b-a4b-it) through the Vercel AI SDK and Google AI Studio does the identification, the fact writing and the walk dispatch.
  • Wikipedia's API verifies species names and supplies the text that facts are written from.
  • MapLibre GL with OpenFreeMap tiles draws the map. OpenRouteService supplies pedestrian loops.
  • ElevenLabs handles speech-to-text for voice notes and text-to-speech for narration.

Gemma does three jobs, and none of them gets the last word

Endpoint What Gemma does What surrounds it
/api/identify Photo in, structured JSON out: is it alive, and up to 3 candidates Each candidate's Wikipedia title is looked up and flagged verified or not. Non-organisms and people are refused.
/api/facts Turns a Wikipedia lead section into a summary, 3 facts and a "look closely" prompt Citation check, safety regex, degeneration filter, Wikipedia-sentence fallback
/api/walk/dispatch Writes a short dispatch and finds ecological links between the species you met Empty connections is a valid answer

Every response is a Zod schema, not free text. This is the identification schema, trimmed:

candidates: z.array(z.object({
  scientificName: z.string(),
  commonName: z.string(),
  kind: z.enum(['tree','flower','plant','bird','insect','fungus','animal','other']),
  confidence: z.enum(['Likely','Possible','Unsure']),
  wikipediaTitle: z.string().describe('Exact English Wikipedia article title for this species'),
}))
Enter fullscreen mode Exit fullscreen mode

Asking for a Wikipedia title is the cheap trick that makes verification possible. The title goes to the Wikipedia Action API. If the article exists, the candidate links to it. If not, it comes back verified: false.

Make the model cite, then check the citations

The facts endpoint never asks Gemma what it knows about a species. It hands Gemma the verified Wikipedia lead section, numbered sentence by sentence, and asks for facts that each cite the sentence they came from:

[1] The white oak (Quercus alba) is one of the pre-eminent hardwoods of eastern and central North America.
[2] ...
Enter fullscreen mode Exit fullscreen mode

A citation with a sentence number outside the extract gets dropped. I want to be clear about what this does and doesn't catch: it checks that the citation is valid, not that the fact semantically matches the sentence. It narrows the room for invention. It does not close it.

There is also a hard rule against edibility, foraging, taste, remedies and toxicity. It lives in the prompt and in a regex that scrubs the output, because "this one is edible" is the one mistake a naturalist app cannot afford to make.

The model's bad days

Small open models sometimes fall into loops. Gemma occasionally produced degenerate text in fact generation: repeated tokens, runaway hyphen chains, tokenizer artifacts leaking into prose. Every generated string now goes through a filter:

// repeated token loops: "pattern-pattern-pattern"
if (/(\b[\w-]{2,}\b)(?:[- \t]+\1){2,}/i.test(trimmed)) return true;
// tokenizer artifacts leaking through
if (/ueff_|exp_\d|\[PAD\]|<eos>|<bos>/i.test(trimmed)) return true;
Enter fullscreen mode Exit fullscreen mode

When a fact fails, it is replaced with a sentence from the Wikipedia extract. When Gemma fails entirely, the card shows the raw verified extract with a visible warning. The user never gets a blank screen or a garbled sentence.

What leaves your device

Treads is local-first, which does not mean nothing ever leaves. Here is the full list:

  • The photo, sent to Gemma for identification.
  • A coarse location (rounded to one decimal place) and the month, so Gemma can weigh what is plausible in your region and season.
  • Species names and your notes, sent to Gemma when a walk ends so it can write the dispatch.
  • Voice audio, sent to ElevenLabs for transcription.

Everything else (the observation database, breadcrumbs, photos, journal and garden) stays in IndexedDB. A first-visit consent gate explains this and requests durable storage with navigator.storage.persist() so the browser doesn't evict your life list.

Loops that actually match the distance you asked for

OpenRouteService returns pedestrian loops, but it does not always honor the target. A request for 1 km can come back as a 4.3 km detour because a highway forces the route. Treads tries alternate seeds, and if the best result is still more than 35% off, it falls back to a synthetic loop generator that hits the requested distance exactly.

The daily hunt, the garden, and small tactile things

The daily hunt is deterministic: the date is hashed with FNV-1a and picks a challenge template, so everyone gets the same hunt with no server involved. Difficulty runs Gentle, Moderate or Explorer. Completing targets triggers a synthesized Web Audio chime (no audio files, just two oscillators) and a haptic pulse where the device supports it. The journal turns your streak and kilometres into a garden of flowers that bloom through stages.

The visual language is bone paper and forest ink, with isometric line figures from Hairline for the hero and empty states, and a handwriting-style reveal that respects prefers-reduced-motion.

How I checked it

pnpm test:eval runs a 30-fixture suite: 20 organisms and 10 things Treads should refuse (mugs, a bicycle, a bench, a phone, human faces and hands). It asserts at least 70% organism accuracy and at least 90% correct refusals.

An honest caveat: the fixtures are 30 SVG illustrations, not field photos. The suite checks the wiring, the schema and the refusal behaviour. It does not tell you how Gemma performs on a blurry photo of a beetle in dappled light.

Why Does Open Innovation Matter?

Treads runs on Gemma 4, served through Google AI Studio, the perfect candidate for this hacktober!

That choice shaped the project in three ways.

  • The model is a component, not a dependency. The model ID lives in environment variables (IDENTIFY_MODEL_ID, FACTS_MODEL_ID), and the Zod schemas and verification layers don't care which model sits behind them. Open weights mean pricing, quotas or terms at one provider don't decide whether Treads can exist. The same Gemma family can be served from Google Cloud, another provider, or hardware I own.
  • The most personal step has a path home. The photo is the most sensitive thing Treads sends anywhere. Because the weights are open, moving that one call to infrastructure I control is a deployment change, not a rewrite. That's my next step, and it's the part a closed vision API could never offer.
  • I could design around a small model's behaviour. Gemma is a compact open model, and it sometimes degenerates: repeated tokens, leaked tokenizer artifacts. Knowing that, I treated its output as untrusted from the start: schema first, verification second, fallback third. That's why the app holds up when the model has a bad day.

I chose a hosted model over an in-browser one deliberately. A multi-gigabyte download and the battery and WebGL risk of running a model mid-walk are the wrong trade for an app whose whole point is a phone that stays in your pocket.

What I Learned

  • Identification is the easy part. The product is knowing when not to answer: refusing mugs and people, labelling confidence, and showing the source.
  • Grounding beats prompting. Handing the model a numbered extract and demanding citations did more for accuracy than any amount of "be accurate" in the prompt.
  • Small models need a bouncer at the door. Schema validation catches malformed output. It takes a separate filter to catch well-formed nonsense.
  • Admit the limits in the write-up. What leaves the device, what the eval does and doesn't prove, and what I haven't built are all stated above on purpose.

Known limits:

  • Identification calls a hosted model, so it needs a connection.
  • If the API key is missing or the model errors, /api/identify returns clearly flagged placeholder candidates so the UI doesn't break. That's a demo-safety choice I'd revisit before calling this production.
  • Candidates are suggestions. Unsure means unsure.

Decions I took (Maybe Plans for future?)

  • Accounts. Save your progress across multiple devices and never lose progress, well also view it on PC. Didn't implement it because I couldn't justify having an auth system on an alpha-esque prototype.
  • Online AI. Local models would drain too much battery and take more time to work, which might make user lose interest in waiting. Also it would require them to download the model itself, so that's another thing
  • Agent Frameworks. Just not enough moving parts to justify it. If more features, or codebase revamp will be done, definately on high priority. The current system gets the job done and does it extremely well so no regrets there

My Agent Session

Sessions by Entire : Sessions

Prize Categories

Best Use of Gemma
Best Use of ElevenLabs
Best Use of Entire: Sessions

Thanks for reading, hope your next walk is much more engaging!

Go outside. Notice. Journal it

Zephyrxx0

Top comments (0)