This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass
What I Built
If your kids are anything like mine, they haven't touched grass lately, and the printable scavenger hunts online don't help: they ask for a pinecone in a city park, or a basketball hoop in a nature preserve. Grass Pass turns your real local park into a one-page, black-and-white treasure hunt for your kid's age, written by the open model Gemma 4 from that park's real map and checked by code. The kid hunts with a pencil. The phone stays in your pocket.
Try it: grass-pass.vercel.app (no sign-up to open or print an example; your first new pass each day is free) · Code: github.com/c0de128/grass-pass (MIT)
I'm Kevin, a dad in Allen, Texas. I built it for my kids Hunter and Sawyer, and Riley, my girlfriend's son. It's for any grown-up with kids aged 4 to 13, plus a 13+ band for teens and adults.
Why not a generic printable?
Because parks aren't generic. Two parks in my town, measured on Oct 5, 2026: Connemara Meadow had 70 wildlife species photographed in 14 days and no playgrounds, courts or shelters on the map. Celebration Park had 25 soccer fields and no recent sightings at all. A generic hunt fails at both.
So each pass uses what is really there: Park Finds from OpenStreetMap, Wild Finds from iNaturalist sightings within 1.5 km in 14 days, Lucky Finds ("Maybe!" a dog) when 3+ recent Google reviews mention them, a Find This Spot map with an X on a real landmark, and real October monarch counts. If a source has nothing, the pass says "No data available".
A real pass from the live site
The live site made this pass for Celebration Park, ages 6-10, on Fri Oct 9, 2026 at 12:27 PM CDT (clues by gemma-4-31B-it). Four of its eight finds, word for word:
2. Point to a roof with pillars that keep you dry in the rain. (1 on the park map · OpenStreetMap)
4. Which flat space has a low net strung across its center? (4 on the park map · OpenStreetMap)
5. Hunt for a sandy area with a white base at every turn. (5 on the park map · OpenStreetMap)
7. Peek at the path that carries hikers above water. Stay with your grown-up near water. (1 on the park map · OpenStreetMap)Find This Spot: "I have a tower you can climb using ladders and steps. What am I?" (the X is 130 m west of START)
October special: 23 monarchs seen Sep 25-Oct 8 vs 59 in 2025.On the grown-up's stub: picnic shelter, tennis courts, baseball fields, bridge; the X is the playground. Find 8 is a Lucky Find: "a dog out for a walk (maybe)", because 4 visitor reviews since Oct 2024 mention dogs.
It has no Wild Finds, and says so: no recent sightings nearby. I'd rather print that than invent a squirrel.
Demo
Live app: https://grass-pass.vercel.app
The 90-second tour, with captions:
Test it in 60 seconds
- Open grass-pass.vercel.app. No login needed.
- Under Explore ("See a real pass, right now."), tap any example park. It opens at once, because the site already made it from live data.
- Press Print pass. You get one black-and-white Letter page (A4 works too): the kid's hunt on top, a cut line, the grown-up's answer key below. "Save as PDF" is fine.
- Make one for your park: type its name, a town or a ZIP, pick a park and an age, and press Make my pass. Your first new pass of the day needs no sign-in. After that, press Try as a judge: one click, no sign-up.
On Oct 9 on the live site, two new passes took about 12 seconds each and printed on one page. Most take 10-30 seconds; busy map servers can push it to about 90.
One honest note: I haven't taken a pass on a family outing for this write-up, so there are no kid reactions or park photos here.
Code
c0de128 / grass-pass
Grass Pass: your ticket to get outside. Printable park treasure-hunt passes built from real park data with open-weight Gemma 4. Hacktoberfest 2026 Week 1.
Grass Pass: Family time is back! Powered by AI.
Your ticket to get outside. Pick a park. Print a pass. Phone away.
Try it live: https://grass-pass.vercel.app (live since Oct 8, 2026). The example passes open with no sign-in. Your first new pass each day needs no sign-in either; for more, press Try as a judge (one click, no sign-up) or sign in with GitHub or Google.
Grass Pass turns your local park into a one-page treasure hunt, usually in 10-30 seconds (up to about a minute and a half for a big park or a slow model; measured, see Limitations). Pick a park and your kid's age (4-6, 6-10 or 10-13; or 13+ for teens and adults). Code collects what is really in that park. Gemma 4 (open weights, Apache-2.0, on DigitalOcean) picks a fair mix and writes kid-level clues, usually in one call (at most 3 clue calls…
One Next.js 16 app on Vercel with Zod and Upstash Redis. 2,671 unit tests run on recorded real API answers, with no mock data. The README has the privacy table, every limit and how to run the evals. MIT licence.
Credits and licences
- Clues, riddles and trip tips: Gemma 4 (
gemma-4-31B-it, Apache-2.0) on DigitalOcean serverless inference. - Eval comparison model: Llama 4 Maverick (Llama 4 Community Licence), on DigitalOcean.
- Self-host test: Gemma 4 E2B (
gemma4:e2b-it-qat, Apache-2.0) on Ollama. - Map data: © OpenStreetMap contributors, ODbL 1.0, via Nominatim and the public Overpass servers (overpass-api.de, maps.mail.ru, overpass.private.coffee).
- Wildlife sightings and monarch counts: iNaturalist observers (research grade; names and counts only, no photos).
- Species facts: Wikipedia (CC BY-SA), via the iNaturalist API.
- Lucky Finds: Google Maps review counts via SerpApi (counts and months only, never review text or names).
- Weather: Weather data by Open-Meteo.com (CC BY 4.0) and alerts from the National Weather Service (US public domain).
- Libraries: Next.js and React (MIT), Auth.js / next-auth (ISC), Zod (MIT), Tailwind CSS (MIT), Vitest (MIT), Playwright (Apache-2.0), axe-core (MPL-2.0), Lucide icons (ISC); asset tools opentype.js (MIT) and resvg-js (MPL-2.0).
- Fonts (SIL OFL 1.1): Bricolage Grotesque, DM Sans, Fredoka, Nunito, M PLUS Rounded 1c and Varela Round.
- Pictures: park photos by Robert Nunnally (CC BY 2.0), Vulturesong (CC0) and Jackilometresan (CC0). The home page hero is an AI illustration made with v0 by Vercel, labelled "AI illustration" on the site. Stock clips in the video are licensed from Magnific; the people in them are not my family.
- Design and build: site design v3 by me in v0 by Vercel; later redesigns and most of the code by AI coding agents (Claude Code) at my direction. Logo banner first made with Google Gemini, then traced as SVG.
- Code from before the contest: a few generic helpers (model client, rate limits, request guards) were adapted from my own unpublished practice project of Oct 2, 2026. Everything specific to Grass Pass was written from Oct 5 (first commit 5:11 PM CDT).
How I Built It
How I used open-source AI
One open-weight model does one job, and plain code does everything else.
| Model | Gemma 4 31B (gemma-4-31B-it), open weights |
| Licence | Apache-2.0 |
| Where it runs | DigitalOcean serverless inference (US), through an OpenAI-compatible API |
| What it does | Picks a fair mix of real items for the park and age, writes the kid clues, the Find This Spot riddle and the trip tips |
| What it never does | Decide what is safe, write a number or a date, or invent an item |
| Calls per pass | Usually 1 for the clues (at most 3, with a retry and refills), plus 1 for the trip tips |
Real data: OpenStreetMap, iNaturalist + Wikipedia, Google review counts (SerpApi) -> code builds the pool: what is really in this park -> code removes anything risky (by species id, before AND after the model) -> Gemma 4: strict JSON, may only pick real item ids, writes clues + a quote from the source -> code checks every clue against its source; a failing clue is dropped, never rewritten -> print: one page, kid's hunt on top, grown-up's stub below What code does, so you don't have to trust the model:
- Safety is code, not a prompt. 71 risky groups (venomous snakes, fire ants, poison ivy and more) are removed by species id and every ancestor id, before and after the model.
- Every clue must quote its source. Each clue's
sourceQuotemust appear word for word in that item's map fact or Wikipedia summary. A clue that names its answer, adds a number or has a link is dropped. - Season check. A flower or fruit clue only survives if people near the park recorded that plant flowering or fruiting this month.
What the AI adds (vs the same app with no AI)
My eval includes a no-AI template: same data and rules, but each clue is the source sentence with the answer blanked out. Same tree:
- No-AI template: "_, commonly known as the _ orange, is a small deciduous tree or large shrub, typically growing to 8 to 15 metres"
- Gemma 4: "Hunt for a tree with bumpy, yellow-green fruit." (Answer: Osage-orange.)
A 7-year-old can use the second one. Across 20 real parks (17 in North Texas, plus Zilker, Central Park and Golden Gate Park), recorded live and replayed through the app's code, ages 6-10:
| Gemma 4 31B (60 runs) | Llama 4 Maverick (20 runs) | No-AI template | |
|---|---|---|---|
| Risky species printed | 0 | 0 | 0 |
| Clues quoting their source word for word | 99.8% | 93.6% | 100% (copied by design) |
| Complete passes (goal 90%) | 90.2% | 64.7% | 17.6% |
| Reading level (grade, median; goal 3.5 or less) | 2.3 | 2.3 | 2.8 |
| Clues repeated across parks (goal 5% or less) | 4.1% | 0% | 20.4% |
| Typical / slow model call (goal 10 s / 20 s) | 9.9 s / 27.1 s | 20.6 s / 57.0 s | none |
| Cost per pass, clue calls (goal $0.001) | $0.00108 | $0.00190 | $0 |
The whole eval run cost $0.09 in model calls. Gemma met 6 of my 9 targets. The misses, honestly:
- Slow calls: the slowest 5% took 27.1 s (goal 20 s).
- Cost: $0.00108 a pass, just over my $0.001 goal (trip tips add one more call).
- The kid check (10 printed clues read as a 7-year-old would) is not done.
- Not in the eval: the map and Lucky Finds. Older age bands only had 3-park checks (3 of 3 complete).
The build: a small team of AI agents and a review loop
I built Grass Pass in one week with Claude Code set up as a small team: an architect agent wrote the spec, builder agents worked in parallel, then five reviewer agents (contest rules, security, code quality, accessibility, a tough DEV judge) checked the whole app. Every finding went back to a builder, and the reviewers ran again.
That loop ran 12 times from Oct 5 to Oct 9. The first round found 9 serious problems, 2 of them bad enough to stop a release. Even after launch the reviewers caught real things: when I added AI trip tips, the security reviewer wrote 24 risky test tips, and my filter let 15 through, including "Go barefoot in the creek". Gemma's recorded tips were clean, but the page promised code checks every tip, so the rules were rewritten and every probe became a permanent test.
Three things that broke, and how I fixed them
1. The first example pass asked for spring flowers in October. "Find a tree with white flowers that have five petals" is a Callery pear. Wikipedia agrees, so my quote check passed it, but it blooms in spring. Quoting a source checks what, not when. Fix: a season check on iNaturalist flowering records. Out-of-season plant clues on printed passes went from 6 to 0 in the next eval run.
2. A pass for 10-13-year-olds asked them to hunt a poisonous plant. In a test pass for a park in Madison, Wisconsin, a reviewer agent got white snakeroot, which its own Wikipedia summary calls "a poisonous perennial herb". It wasn't on my blocklist. Fix: snakeroot and the hemlocks went on the list, and code now drops any species whose own description says poisonous, toxic, venomous, stings or burns, whatever the model writes.
3. Typing my own ZIP code found parks in Paris. A bare "75013" is also a Paris postcode, so a reviewer got "Parks near 75013, Paris" on the live site. Fix: a bare 5-digit ZIP is now looked up as a US ZIP code. Now "75013" lists parks in Allen.
Why Does Open Innovation Matter?
Here is what open weights made possible for this app, measured:
| What open gave me | What I measured |
|---|---|
| My safety rules, not a vendor's. Every check lives in my code and runs the same on any model. Switching models is one setting. | Llama 4 Maverick ran through the exact same checks: 0 risky species printed, but only 64.7% complete passes and a 20.6 s typical call, so Gemma stayed the default. |
| I can run it myself. The app talks to any OpenAI-compatible server, Ollama included. | The small Gemma 4 E2B on my laptop's CPU (no GPU, $0): 4 of 5 test passes complete, 96.7% of clues quoting their source, 0 risky species. In the app, one try made a 7-of-8 pass in 96 s. |
| Cheap enough to give away. Pay-per-token, no subscription. | $0.00108 a pass; the whole eval cost $0.09. Passes are cached per park, age and day, so the next family there costs nothing. |
| It can't be taken away. Gemma 4 is Apache-2.0. | If my host changes prices or closes, the same weights run somewhere else. |
The limits, honestly. Self-hosting is slow: with the app's normal time limit the laptop made 0 of 5 complete passes; it needs 1-3 minutes a pass. The live app isn't offline: the park and age band go to DigitalOcean (nothing about the child is asked for or sent). And I can't claim open beat closed: the closed models on my plan answered "403 forbidden" on Oct 5, so I compared open models only.
What I like most about building it in the open is the stub at the bottom of every page. The kid gets a pencil and an X on a real map. The grown-up gets a strip of paper that names every source, the date it was checked, and the open model that wrote the clues. Nothing on that page asks you to take it on trust.
Prize Categories
- Best Use of Gemma: Gemma 4 31B writes every clue, riddle and trip tip. It beat Llama 4 Maverick on my 20-park eval, Gemma 4 E2B ran through the same checks on a laptop CPU, and Gemma redrafted the site's own copy: 184 blocks sent, 90 shipped (80 still on the site).
- Best Use of DigitalOcean: every model call the live app makes runs on DigitalOcean serverless inference, and so did the two-model eval ($0.09 a full run). (The website itself is hosted on Vercel.)
- Best Use of SerpApi: Lucky Finds. SerpApi's
google_mapsengine matches the park, thengoogle_maps_reviewscounts recent reviews that mention dogs, bikes, ducks or skateboards. Three or more mentions become a "Maybe!" find with the count printed as evidence, like the Celebration Park dog above (4 reviews). Review text is never shown or sent to the AI.


![Why Object.keys(user) Returns string[], Not (keyof User)[]: TypeScript Means It](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2q4w48b9bzs43tynyhsf.png)
