Nature by Friends: A Kids’ Nature Adventure That Puts the Phone Down
Built for the Hacktoberfest Open-Source AI Challenge — Week 1: Touch Grass. 🌱
Almost every app I use quietly asks for one more tap. I wanted to build the opposite: an app for children whose best moment happens when the phone goes face-down on the grass.
Nature by Friends is a storybook nature adventure for children aged 6–10 and the grown-ups who explore the outdoors with them. Its guiding rule is woven into every screen:
Become a friend, not an owner.
No catching. No collecting. No points, streaks, or timers. A child doesn't succeed by finding the rarest bug. They succeed by noticing something, quietly, from a respectful distance.
The app is here to start an adventure—not become the adventure.
The welcome page. The real discoveries happen outside; the app just points the way.
What I Built
🌿 The screen is the shortest part
Every adventure follows a simple, child-friendly loop:
- Meet a friend. Choose one of 20 hand-drawn neighbours: a frog, a snail, moss, a mushroom, a crow, and more.
- Discover a tiny mission. For example: “After rain, look on walls, paths and leaves for a shiny trail.”
- Respect its boundaries. Don't touch, chase, or take anything home. Never pull a snail's shell.
- Phone down. Eyes up. Go outdoors with a trusted adult. There's no timer; the app can be closed, and the mission will still be waiting when the child returns.
- Reflect. What did you notice? What surprised you? What would you like to remember?
- Keep the memory. The answer becomes a page in a nature journal saved on that device.
Not finding anything is a valid ending, too. The app really says:
That's completely okay — friends don't always show up.
I wanted children to feel curious, not unsuccessful, when nature doesn't appear on demand.
Step 4 of every mission. This is the screen I most wanted to get right.
🌼 Nature Garden
Twenty illustrated nature friends, each with real facts, habitat information, safe ways to observe them, and a clear list of things not to do.
🎧 Listening Corner
- Sound Safari: Hear a creature's call, guess who made it, and discover a fact about the animal.
- The Quiet Minute: A guided outdoor listening exercise with real moments of silence.
📸 Photo Helper
A grown-up can optionally ask Gemma 4 for a cautious identification guess from a photo. The wording stays tentative—“might be”—because an AI guess should never be treated as certainty.
The image is reduced and re-encoded on the device to remove GPS/EXIF metadata before it is sent to Google's Gemini API. The grown-ups' page explains the photo flow and asks adults to submit photos of plants and animals only, never people.
🕸️ Friendship Web
Thirty-seven hand-written connections link the 20 nature friends with the sun, a pond, fallen leaves, soil, and one another. Children can explore how living things depend on their surroundings.
📖 Tiny Nature Stories
Twenty short stories mix imagination with learning. 🍃 marks a real nature fact; ✨ marks an imaginative element.
📔 Nature Journal
Children can record return visits and revisit earlier observations through a Then and now comparison. Gentle indicators show friends met and sounds learned, but the journal never turns nature into a competition.
👨👩👧 For grown-ups
A plain-language page covers outdoor safety, how the AI photo helper works, and privacy. It also includes a Delete all my data option.
Every friend has a story, useful facts, boundaries to respect, and a way to listen when appropriate.
Demo
🌐 Live app: https://nature-by-friends-axjw.onrender.com
🎬 60-second demo film
The film uses real product footage and the same ElevenLabs narrator voice used inside the app.
Prefer a hands-on, two-minute tour?
Open the live site. A guided tour appears automatically. Select Show me around to hear it narrated, or choose Skip tour to explore independently.
Try these paths:
- Mission: Open the Garden Snail mission, select Got it, then I promise to be a friend. You're at “Phone down. Eyes up.”
- Listening: Open the Listening Corner and start Sound Safari. Turn your sound on and guess who's singing.
- Connections: Open the Friendship Web, tap the Frog, and explore Tadpole nursery.
Code
💻 GitHub repository: https://github.com/anshvarshney1502/Nature-by-Friends
The repository includes the application code, safety rules, tests, audio-generation scripts, and implementation details.
How I Built It
I built Nature by Friends during this challenge week with Claude Code as my pair programmer, working in the terminal and desktop app.
I set the direction, designed the experience, defined the safety rules, and made the product decisions. Claude Code helped write and test the code, use the browser to check the live site, and work with Render and MongoDB Atlas through their connectors.
The main engineering principle was simple: AI can support the experience, but it must not decide what a child should do outdoors.
The architecture
Child + trusted adult
|
Next.js 16 app
on Render
|
+----------------+----------------+
| | |
Audio clips Curated content Photo helper
(static files) (MongoDB Atlas) (Gemini API)
| | |
ElevenLabs Read-only user Gemma 4
generated + local fallback + sanitizer
|
Journal + progress in the
browser's localStorage
The main routes are:
-
/api/identify— calls Gemma 4 through the Gemini API, then validates and sanitizes the response. -
/api/audio— serves narration only for sentences already present in the app's curated content. -
/api/content— retrieves curated nature profiles from MongoDB Atlas, with a bundled fallback. -
/api/health— reports which services are configured.
The journal and progress data stay in browser localStorage; they are not sent to the database.
🎙️ ElevenLabs: one consistent voice
Children aged 6–10 aren't all confident readers. A mission they can hear is a mission they can understand before putting the phone away.
I used ElevenLabs Text-to-Speech (eleven_multilingual_v2) for:
- Read-aloud narration across all 20 profiles, missions, and stories.
- A narrated nine-slide guided tour.
- Sound Safari answers and explanations.
- The Quiet Minute.
The Sound Effects API provides three seamless, looping soundscapes: a chirpy garden, rain on soil, and evening crickets. Pages can change their atmosphere to match the experience.
Nature friends also have their own calls where appropriate: buzzing, chirping, cooing, cawing, croaking, and chattering. For quieter friends such as worms and moss, the app plays the sound of their surroundings and labels it honestly.
Audio engineering highlights:
-
110 clips generated once: 87 voice clips and 23 sound effects. The
npm run audio:generateandnpm run sounds:generatescripts create missing files and skip files that already exist. - No per-visit generation cost: Visitors play static audio files, so listening doesn't trigger fresh ElevenLabs generation.
-
Curated narration only:
/api/audioaccepts sentences already present in the app's curated content instead of arbitrary text that could be used to generate unlimited narration. - One voice at a time: Read-alouds, tour narration, and clips share a voice channel, preventing audio from talking over itself. The garden soundscape ducks under the narrator.
- A fallback for accessibility: If a clip cannot play, the device's speech voice can take over, and the text remains visible.
The demo film was narrated with that same ElevenLabs voice, generated specifically for the video.
Sound Safari makes listening an act of discovery, not another high-score challenge.
☁️ Render: one service, carefully hardened
The pages, four API routes, and all 110 audio files run as a single Render web service. Deployments happen automatically whenever changes are pushed to main.
I focused on security and reliability as part of the build:
- A health check at
/api/health. - Cloudflare-aware rate limiting using
CF-Connecting-IP, rather than trusting a client-suppliedX-Forwarded-Forheader. - Global caps on paid API endpoints to help protect the Gemini quota, even if requests come from rotating IP addresses.
- Origin checks for POST requests and request-size limits.
- A strict Content Security Policy and HSTS on responses.
- Secrets held in Render environment settings instead of being committed to the repository.
I found that spoofing X-Forwarded-For could bypass my original rate limiter. I changed the implementation to use Cloudflare's client IP header and added global caps to the paid endpoints.
I also scanned the full Git history and built browser bundles for exposed keys.
Gemma inference does not run on Render. Render hosts the application and API routes that wrap the model call.
📸 Gemma 4: a careful helper, not an oracle
The photo helper has a deliberately narrow job. A grown-up chooses a photo and selects Ask about this photo. The image is reduced and re-encoded on the device before the /api/identify route sends it to gemma-4-26b-a4b-it through the Gemini API, requesting structured JSON.
The model's response is never trusted blindly:
- Zod validates the response.
- Species without corresponding curated content are dropped.
- An identification without a usable name becomes “We're not sure.”
- Potentially risky finds—including wasps, spiders, and mushrooms—trigger a message to stop and tell a trusted adult.
- Suggestions to touch, catch, chase, feed, or dig are replaced with a pre-written mission.
Safety advice comes from my own curated content, not from the model.
If Gemma 4 fails, the application retries once using gemini-flash-lite-latest. During live testing, the hosted Gemma 4 endpoint occasionally returned HTTP 500 errors, so the fallback helps the feature handle those failures.
The wording is intentional: an AI-generated identification is a possibility, not a promise.
🍃 MongoDB Atlas: the curated content layer
The 20 nature profiles live in MongoDB Atlas in the nature_profiles collection. The collection has a $jsonSchema validator and a unique slug index.
The app reads profiles through /api/content, connecting in production with a read-only database user scoped to one database on one cluster. I tested that it can read the intended content while writes, collection drops, and reads from other databases are denied.
If Atlas becomes unreachable, the app falls back to an identical bundled copy of the content. A temporary database problem should not break a child's mission.
Children's journals and progress never go to Atlas; they remain in each browser's localStorage.
🧰 The tools behind the build
- Product and frontend: Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4, Motion, and Zod.
- Voice and sound: ElevenLabs Text-to-Speech and Sound Effects APIs.
- AI: Gemma 4 through the Gemini API, with Gemini Flash-Lite as a fallback.
- Data: MongoDB Atlas.
- Hosting: Render, behind Cloudflare.
- Quality checks: Vitest, with 59 passing tests covering safety rules, the model-output sanitizer, API routes, the journal, progress tracking, the voice channel, and content integrity. GitHub Actions runs linting, type checks, tests, a production build, and a smoke test of the built server on every push.
- Agent and tooling: Claude Code, Render and MongoDB Atlas connectors, the built-in browser for live testing, and GitHub CLI settings for secret scanning, push protection, and Dependabot.
-
Demo production: Claude Code's
/bragskill and HyperFrames, with Headless Chrome recording the real application, ElevenLabs narration, and FFmpeg finishing the video at 1920 × 1080 and normalizing audio to −14 LUFS. - Art: 20 original, hand-built SVG characters—no stock images and no emoji used as character artwork.
Why Does Open Innovation Matter?
For a children's nature app, the model is the least important part. That is precisely why I wanted the project to be open.
Open weights help keep the AI replaceable.
Gemma 4 performs one narrow task behind a strict boundary: suggest, never decide. The facts, safety boundaries, and missions that guide a child's behaviour are hand-written and live in the repository, not inside a model.
The same open-weight model can be served through different providers or, where hardware and deployment requirements allow, on a school's own machine. In this codebase, the provider integration is isolated in lib/ai/gemini-provider.ts. Changing that integration does not require rewriting the safety layer around it.
I explored a fully self-hosted setup first. I wrote a small Docker configuration running Ollama and Gemma behind an authentication proxy on a Hugging Face Space, aiming to keep photos on infrastructure I controlled. The free Docker Space route required a paid plan, so this challenge version uses the same open model through the Gemini API instead.
The application currently uses a hosted API for photo identification, but the open weights keep other deployment paths available. The goal is to make a future change in where inference runs a provider and deployment decision—not a complete rewrite of the product.
Open source also makes the boundaries checkable.
Every safety rule, sanitizer step, and all 59 tests are public. A parent or teacher can inspect what the application will and won't tell a child instead of having to trust a black box.
The Friendship Web helps children see nature as a network of relationships, not a collection of things to own.
Prize Categories
🏆 Best Use of ElevenLabs
One consistent narrator using eleven_multilingual_v2 powers read-alouds, the guided tour, Sound Safari reveals, and the Quiet Minute. The Sound Effects API provides living soundscapes and creature sounds. All 110 clips are pre-generated, and the 60-second demo film uses the same voice.
☁️ Best Use of Render
The Next.js frontend and API routes run as one auto-deploying Render web service, with a health check, Cloudflare-aware rate limiting, global quota caps, origin checks, and a strict Content Security Policy.
📸 Best Use of Gemma
Gemma 4 (gemma-4-26b-a4b-it), served through the Gemini API, powers the optional photo helper. Every response is schema-validated and safety-sanitized, with a Gemini Flash-Lite fallback.
🍃 Best Use of MongoDB Atlas
Atlas stores the curated nature profiles with schema validation and a unique index. The production connection uses a least-privilege, read-only user, and an identical bundled fallback protects the experience when the database is unavailable.
🌱 The goal was never more screen time
Nature by Friends is my attempt to build technology that knows when to step aside.
A child might open the app because a frog looks interesting. They might leave having noticed a trail on a wet wall, listened to a bird they had never really heard, or learned why a pond matters to more than one creature.
And then they close the app.
No streak to maintain. No rare creature to collect. No timer telling them when to stop looking.
Just a small invitation to notice the world that was already there.
The goal was never more time with an app. It was more time noticing the frog by the pond. 🐸
🌐 Live app: https://nature-by-friends-axjw.onrender.com
💻 Open-source repository: https://github.com/anshvarshney1502/Nature-by-Friends
🎬 60-second demo: https://youtu.be/to063BGyCTw
Built for the Hacktoberfest Open-Source AI Challenge — Week 1: Touch Grass.






Top comments (2)
Great App!! Thoroughly planned and well executed 🔥
❤️❤️
Some comments may only be visible to logged-in visitors. Sign in to view all comments.