This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass
What I Built
Most apps that promise to get you outside still want you to keep looking at them. Step counters, route apps, nature feeds: they all count on your attention.
I wanted the opposite. Touch Grass is a one-screen planner that tries to be the shortest part of your day outside.
You tell it four things:
- where you are (it defaults to Chennai, where I live)
- how long you have: 15 minutes, 30, an hour, two
- what you feel like: birding, walking, gardening, the beach, cycling
- what it's like out there: sunny, cloudy, very hot, drizzling, or a proper storm
An open-weight model running on your own device writes a three-step plan that fits the time you have, plus one seasonal tip: which birds are around this time of year, or what to plant this week. Then you press Go outside.
The screen turns dark green and shows only a big timer, a short "bring this" checklist, and the plan in small print. That's the whole idea: read it once, put the phone in your pocket, and go. When you're back you press I'm back, and if you like, type one line about what you saw. The app keeps a tiny log ("This week: 3 times outside, 85 min") that never leaves your phone.
It's for people like me: students and office workers in a big, hot, busy city who mean to go outside and then lose an hour scrolling. It's built around Chennai's seasons, because a plan that ignores the northeast monsoon or a 38°C May afternoon isn't a useful plan here.
Demo
Try it: https://ess-cor.github.io/touch-grass-chennai/
It works best in a recent Chrome or Edge on desktop or Android, where WebGPU is available. The first time, you choose a model to download:
- Gemma 2 2B Instruct (about 1.4 GB, the default and the better planner)
- Qwen2.5 0.5B Instruct (about 270 MB, a "lite" option for phones with less memory)
After that it starts from your browser's cache, with no signal needed. If your browser has no WebGPU, you still get a plan from the built-in rule-based planner, so the app never just says no.
Code
Touch Grass 🌿
Plan in 30 seconds. Spend the rest outside.
Touch Grass is a one-screen outdoor planner. You tell it where you are, how long you have, what you feel like (birding, walking, gardening, beach, cycling) and what the weather is doing. An open-weight model running inside your browser gives you a three-step plan and one seasonal tip. Then you press Go outside, the screen turns into a plain timer and checklist, and the idea is that you put the phone away.
Live: https://ess-cor.github.io/touch-grass-chennai/
Built for the DEV Hacktoberfest Open-Source AI Challenge, Week 1: Touch Grass.
What makes it work
- On-device model. WebLLM (Apache-2.0) runs the model on your GPU through WebGPU. The default model is Gemma 2 2B Instruct (
gemma-2-2b-it-q4f16_1-MLC, or theq4f32build on GPUs withoutshader-f16). There is a lite option, Qwen2.5 0.5B Instruct, for phones with less memory. - Nothing…
It's plain HTML, CSS and JavaScript: no build step, no framework, no backend. MIT licensed.
How I Built It
The model runs in the browser tab
The core is WebLLM from the MLC team, an open-source (Apache-2.0) engine that runs open-weight models on your GPU through WebGPU. There's no server anywhere in this project. GitHub Pages serves static files, and everything else happens on your device.
Loading a model is a few lines:
const webllm = await import("https://cdn.jsdelivr.net/npm/@mlc-ai/web-llm@0.2.85/lib/index.js"); const engine = await webllm.CreateMLCEngine("gemma-2-2b-it-q4f16_1-MLC", { initProgressCallback: (p) => showProgress(p.progress), }); Before that, the app asks the GPU what it can do. Not every GPU supports 16-bit floats in shaders, so it picks the right build of the same model:
const adapter = await navigator.gpu.requestAdapter(); const id = adapter.features.has("shader-f16") ? "gemma-2-2b-it-q4f16_1-MLC" : "gemma-2-2b-it-q4f32_1-MLC"; The reply is streamed token by token, so you see the plan forming instead of staring at a spinner.
Why Gemma 2 2B, and a lite fallback
I wanted the smallest model that could still follow a strict format and write a friendly sentence. Gemma 2 2B was the sweet spot: small enough to download once and run on a decent laptop or phone, good enough at short, warm instructions. For phones that can't spare that much memory, there's a button for Qwen2.5 0.5B. Swapping models is one string, because both are open and WebLLM ships builds of both.
One small thing I learned: Gemma's chat template has no "system" role, so all the instructions go into the user message. It works fine.
Small models make things up, so I give it the facts
Ask a 2B model for "birds to look for in Chennai in October" and it will happily invent a species or two. That's not OK in an app that's sending you out to look for them.
So the app keeps a small, hand-written file of seasonal notes for Chennai: four seasons, with birds you can expect, what's good to plant, the weather to plan around, and some well-known green spaces (Pallikaranai Marsh, Guindy National Park, the beaches). The prompt passes in only the notes for this season and tells the model to use only those:
Local facts (use ONLY these for birds, plants and places; do not invent others): - Season: Northeast monsoon (Oct–Dec). Chennai's main rainy season. Check rain alerts before you head out. - Birds around now: Blue-tailed Bee-eater, Grey Wagtail, Brown Shrike, Wood Sandpiper, ... - Good to plant now: a native tree sapling (neem, pungai), greens, radish, coriander, tomato seedlings For any other city, the prompt says the opposite: "You have no verified local data for this city. Do NOT name specific bird species, plants or places." I'd rather give someone in another city general advice than confident nonsense.
The prompt also carries a few safety rules that matter here: keep it short in the heat, suggest a balcony or doorstep version during a storm, and never suggest swimming in the sea.
A fixed format, and a plan B
The model has to answer in a fixed shape:
PLAN: 1. ... 2. ... 3. ... TIP: ... BRING: water, cap, slippers A small parser turns that into the plan, the tip and the checklist. If the model wanders off format, or WebGPU isn't there at all, the app falls back to a rule-based planner built on the same seasonal data. It's less chatty, but it always produces a sensible plan: how long to stand still when birding, how far to walk out before turning back, which birds to look for, what to sow this week. The source line under each plan says which one you got, so it's never pretending.
Offline on purpose
A service worker caches the app and the WebLLM library, and WebLLM keeps the model weights in the browser's cache. The app also remembers which model you loaded and starts it automatically from cache next time. So once you've set it up on Wi-Fi at home, it opens and plans with no network at all. The timer works off timestamps rather than counting seconds, so it stays correct even if your screen is off for the whole walk.
I couldn't test WebGPU inference in my build environment, so the AI path has to be checked in a real browser. I did check that every file parses and that the full non-AI flow works end to end: plan, go outside, come back, log.
I built this with help from an AI coding assistant, then made the decisions about the model, the grounding and the scope myself.
Why Does Open Innovation Matter?
For this project, open weights aren't a nice extra. They're the only reason the idea works.
It runs where the signal doesn't. A planner that needs an API call is useless the moment you're at the edge of a marsh or on the beach with one bar. With the model on the device, the whole thing works offline after the first load. A closed API can't do that.
Your location stays yours. The app knows your city, when you go out, for how long, and the notes you write about what you saw. That's a fairly personal picture of someone's routine. Here, none of it is sent anywhere. There's no server to send it to.
It costs nothing to run, for anyone. I'm a student. If every plan was an API call, I'd either have to charge people, show ads, or shut it down when my free credits ran out. With open weights and GitHub Pages, it costs me nothing to host and costs the user nothing to use, however many people use it.
I can see and change everything. I picked the model size to fit a phone, swapped in a lighter model with one line, and wrapped it in my own prompt and my own facts. If someone in Bengaluru or Kochi wants their city's birds and planting calendar, they can fork the repo and add a season file. They don't need anyone's permission or anyone's API key.
There are honest trade-offs. The first download is big, WebGPU isn't on every phone yet, and a 2B model is nowhere near as clever as the biggest closed models. But for "give me three good steps to get outside in the next 30 minutes", it doesn't need to be. It needs to be private, free, fast, and there when you have no signal. Open models are.
Prize Categories
Best Use of Gemma: Gemma 2 2B Instruct is the default model and runs fully on-device in the browser through WebLLM. It writes every plan and seasonal tip, grounded in local seasonal data.
