Daily Dose of Nature: a private, offline-first nature mission just for you
---
title: "Daily Dose of Nature: a private, offline-first nature mission just for you"
published: true
tags: devchallenge, hf26challenge
cover_image: dev-to-uploads.s3.us-east-2.amazonaws.com
---
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass
What I Built
Daily Dose of Nature is an installable, phone-first web app that answers one question: what is worth seeing around me right now, in the time I have? I started it on October 7 for this challenge.
You pick how long you have (a 15-minute break to an afternoon) and where you are. The app works out how far you can walk and back, looks at the season, the time of day and the weather, and gives you one mission: a named park, garden, wood or waterside spot within reach, plus a short list of plants, fungi, birds and other animals that people have actually recorded around there at this time of year. The briefing tells you the direction and distance in plain words, then tells you to put the phone away.
The best spot doesn't have to be where you start. The app looks at everything inside your walking radius and sends you to the reachable place with the strongest observed species. You can find things on the way, too.
It is for people who would like to go outside but have no plan, no energy, or bad weather as an excuse. For now, it covers only Berlin to optimise the data as much as possible.
Demo
Live app: dailydoseofnature.onrender.com
Code
{% embed kerstinSimoneM/dailydoseofnature %}
MIT licensed. Data and image licences are in the README and in the app's About page.
How I Built It
Data, built once and shipped with the app. A Python pipeline in /pipeline turns open data into a small static pack:
- Species and sightings: 30,850 filtered iNaturalist research-grade observations from a GBIF download (DOI), covering 230 taxa (110 plants, 78 invertebrates, 22 fungi and lichens, 17 birds, and a few mammals and reptiles). It is a download, not a scrape of the API. Only CC0 and CC BY records are accepted.
- Where to go: 7 hand-picked destinations plus about 500 named parks, gardens, woods and waterside spots from OpenStreetMap, merged and de-duplicated by a second script.
- Weather: Open-Meteo forecasts, used for safety rules.
Citizen-science data records where people look, not only where species are. The pack therefore stores aggregate rates per H3 hex cell, shrunk towards a baseline when a cell has few observations, and it flags low-effort cells. It contains no exact occurrence coordinates.
On the phone, everything local. TypeScript, React, Vite and a PWA service worker, with no backend, analytics or account. Mission generation is deterministic:
1. Turn your time budget into a walking radius (60% of the outing is walking, with a detour factor).
2. Keep only public destinations whose round trip fits the budget.
3. Apply safety rules: only street trees in a storm, only lit places on ice or after dark, only shaded spots in heat.
4. Among the reachable places, choose the one whose surroundings have the strongest observed species for this season and time of day.
5. Rank the species for that place.
Directions without GPS. The briefing gives the compass direction. While the sun is up, SunCalc adds a hint computed on the device, such as "keep the sun on your right". After dark, the hint is dropped.
Open-weight model, optional. An English-only experiment runs SmolLM2-360M-Instruct (Apache-2.0) through WebLLM and WebGPU, entirely on the device. It sees only the season, weather category, time budget and candidate species IDs, never coordinates or place names. Its output is validated against the supplied candidates, and it can't override safety guidance. It is a roughly 200 MB download that starts only when the user taps "Try optional AI mission".
An honest result. In my browser tests the model did not clearly beat the handcrafted briefing. Its objectives didn't adapt reliably to the outing length or add real ecological relationships. So the deterministic mission is the default and the AI stays opt-in, until the data has richer habitat and ecology information. The README says the same.
Privacy by design. Location is requested only when you tap "Use my location", is used in memory for ranking, and is never sent anywhere. The forecast always uses a fixed Berlin-centre point, so no request contains your position. There are no map tiles, and a strict CSP limits what the app may contact. Checklist entries store only a species ID and a date.
Photos and credits. Species photos come from iNaturalist and Wikimedia Commons. They are for non-commercial use only, and each one is credited in the app with its author and licence (CC BY, CC BY-SA, CC BY-NC variants, or public domain). A pipeline script (prepare_species_images.py) rebuilds the credits file from a source table, so a photo can't ship without its attribution. Where I resized a photo, the credit says so.
Testing. 56 unit tests (engine, briefing, data pack), 26 Python pipeline tests, and a Playwright end-to-end test that completes a mission. A regression test covers the "no destination fits this outing" bug, which came from having only 7 destinations: at the default 40 minutes only 1 of 28 start areas had a place in reach. With the OpenStreetMap places, 25 of 28 do.
Known limitations.
- Suggestions summarise past observations. They are not live sightings, calibrated probabilities or guarantees.
- Observer bias is reduced, not removed.
- After dark, or in storm, ice or heat, the choice falls back to the 7 hand-picked places, because the OpenStreetMap ones have no lighting or shade data.
- Added to the iOS Home Screen, location access can be blocked by a platform quirk. The area picker is the workaround.
- Berlin only.
I took it outside
On Sunday evening, I wanted to finish the app first, so I went late, but I made it outside!
With 40 minutes, the app suggested a nearby small park. With a short 10-15 minute break, it put noticing the nature around me ahead of walking somewhere.
It was already dark, so I didn't see any of the species it suggested. I did smell wet leaves on the ground and hear a bird singing, though only briefly.
The app hadn't warned me about the dark, because it only counted "night" an hour after sunset. That's fixed now, and it shows a safety note as soon as part of the outing is after dark.
Why Does Open Innovation Matter?
- Your location stays yours. A cloud API would need your position to give a local answer. Here the ranking runs on the phone, and you can check that in the open code.
- It costs nothing to run. It is a static site with no server, key or per-request bill. Open data (GBIF/iNaturalist, OpenStreetMap, Open-Meteo, SunCalc) feeds it, and the sources are credited in the app.
- Open data made the fix possible. When the app kept saying "no destination fits", OpenStreetMap's open licence let me add about 500 real places in one afternoon, and coverage went from 1 of 28 start areas to 25 of 28.
- The model is swappable and optional. The app works without it. Because the weights are open, I could pin an exact revision, check its licence, run it on the device and judge it against the baseline. That is how I found it didn't help yet.
- Anyone can rebuild it. The pipeline can be pointed at another city, and every recommendation can be traced to the data.
Prize Categories
- Best Use of Render: the live app at dailydoseofnature.onrender.com is a Render Static Site. Render builds it with npm ci && npm run build and serves the PWA and its data pack over HTTPS, so the app needs no server of its own.
- Best Use of GitHub Copilot: I built this project with the GitHub Copilot agent in VS Code, in a chat session working directly in the repo. One example: when users kept seeing "no destination fits this outing", Copilot checked the real data and found that only 1 of 28 start areas had a destination in reach at the default 40 minutes, because the app knew just 7 places. It wrote a pipeline that pulled about 500 named parks, woods and waterside spots from OpenStreetMap, then added a regression test. Coverage went to 25 of 28 areas. Copilot also helped add the on-device sun-direction hint, and it kept the 56 unit tests, the Python tests and the end-to-end test green as I changed things.
LumiRoam: the tour guide for beginner photographers
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass
What I Built
I'm a beginner photographer. I have an entry-level Canon, I know roughly what aperture and shutter speed do, and I've lost count of the times I've stood in front of something beautiful at the wrong hour, from the wrong side, with no idea where to start. Every AI tour guide I tried would happily tell me about the building. None of them helped me learn how to photograph it.
LumiRoam is a tour guide for beginner photographers. You tell it where you're going, when, and what camera you carry. It plans a short photowalk of four stops, and for each one it gives you:
- where to stand, often the exact spot where someone took a photo now on Wikimedia Commons
- which way to face, and whether the light will be in front of you, beside you or behind you, which is the thing I most often get wrong
- a focal length that fits your camera and your lenses ("about 34mm on your 18-55", or "wider than your 18mm, step back 13 m")
- a starting point for your settings, with the reason behind it, so you know what to change when the light changes
- a little history of the place, told only from its Wikipedia article
- one child-like crayon drawing of the shot to aim for
It doesn't take the photo for you, and it isn't meant to. I don't want an app that hands me the "perfect settings", because then I'd never learn to see light myself. So the settings are a place to start, not a recipe: the plan explains why ("the sun is behind you, so the front is evenly lit"), and the tip is about composition ("get low and aim up to make the dome feel massive"). After the first frame it's on you to look at the back of the camera and adjust. The drawing is deliberately rough, a crayon sketch of the idea rather than a picture to copy.
Then you put the phone in your pocket. That's the "touch grass" part. Every 75 metres, or every three minutes, LumiRoam speaks into your earbuds:
> "Your planned shot of Bara Gumbad is about 200 metres straight ahead, to the south."
> "You're at your planned spot for Sikandar Lodhi's grave. Sikandar Khan Lodi was the Sultan of the Delhi Sultanate from 1489 to 1517… Face south-east, that's on your left. Try a low angle, looking up at the structure to make it feel more imposing."
In between it tells you short stories about what you're walking past. The screen gets about a minute, at the start. The rest is the walk, and the camera.
Demo
{% embed youtu.be %}
Live app: lumiroam.onrender.com (free tier: if it's been asleep, the first load takes about half a minute)
The screens below are from the live app, planning Lodhi Garden for 17:30 with a Canon EOS 250D:
!The plan: sunset time, golden hour window, and four numbered stops on the map
!A stop card: drawing, history, where to stand, lens, settings, tip
!The walk diary: route on the map, distance, stories heard, and a replayable clip for each one
Here's what a real plan for Lodhi Garden, New Delhi, at 17:30 came back with:
| Stop | The drawing Gemma asked for | History (from Wikipedia only) |
|---|---|---|
| Lodhi Gardens | "Light through leaves": an old tree with low sun through its canopy | A 90-acre city park full of Sayyid and Lodi dynasty monuments |
| Bara Gumbad | "Dome from below": the great dome from a low angle against an orange sky | Built in 1490 CE, with the earliest full dome in Delhi |
| Shish Gumbad | An upward detail of its arches | The "glazed dome", a Lodi-dynasty tomb from around 1494 |
| Sikandar Lodi's tomb | The tomb as a silhouette against the sunset | Sikandar Lodi ruled the Delhi Sultanate from 1489 to 1517 |
Code
{% github stealth-raptor/LumiRoam %}
How I Built It
Built on the commons
Almost everything LumiRoam knows comes from people who shared their work with everyone:
- OpenStreetMap volunteers mapped the tombs, paths, parks and viewpoints. That's how the app knows what's physically there, and the map you see is theirs too.
- Wikipedia editors wrote the history. The guide is only allowed to retell that text, never to add a name or date of its own, so every story can be checked.
- Wikimedia Commons photographers uploaded their photos under free licences, and many recorded where they stood. That's how LumiRoam can say "stand here" and mean a spot a real person already proved. Every reference photo is credited to its photographer in the app.
None of these needs a key, a contract or a fee. That's what made a project like this possible for a beginner in a few nights.
The one rule: code computes, Gemma explains
A language model that says "use f/8 and stand to the south" sounds confident whether or not it's right. So everything that can be calculated is calculated by plain code, and Gemma only explains it:
- The sun. suncalc gives the sun's height and direction at each stop for that exact moment. That names the light: blue hour, golden hour, low sun, high sun.
- Where to stand. If a geotagged Commons photo records where its camera was, 30 to 300 metres from the subject, you stand there. Otherwise the app picks a spot 45° off the sun, at a distance that fits the subject's size.
- Which lens. From the subject's size and your distance, it works out a focal length that fits, converts it for your sensor (1.6× for Canon APS-C, 1.5× for other APS-C), and checks it against the lenses you own.
- A starting exposure. The old sunny-16 rule as code: a base brightness for the light, minus the cloud forecast. It's a first guess for you to correct, not an answer.
All of that goes into a fact sheet for each stop. Gemma's job is to turn it into friendly sentences, explain the reasons, write the history from the Wikipedia text, and look at the photos. I learned why this rule matters the hard way: Gemma once "improved" my computed lens advice into "zoom out to 15mm" on a lens that physically stops at 18. Since then, the lens line is shown exactly as computed.
Mastra runs it as a workflow, not a free-roaming agent
Planning is a Mastra workflow with five fixed steps:
locate → find spots → write plans → fallback sketch → save
A smaller open model left to decide by itself which of six tools to call is unreliable. A workflow keeps the order fixed and puts Gemma where it's strong.
- Locate. OpenStreetMap's Nominatim finds the place. The time zone is looked up offline from the coordinates, so the sun maths never depends on an API. Weather comes from Open-Meteo, with MET Norway as a backup.
- Find spots. OpenStreetMap says what's physically there. Wikipedia says what's notable, and how many people read about it in the last 30 days. That popularity signal alone turned my London test from "a medieval postern and a priory ruin" into "Tower Bridge, the Monument, HMS Belfast".
- Write plans. All four stops go to Gemma 4 in a single call: four Commons photos plus four fact sheets. Because it sees the whole walk at once, it can make the four drawings different from each other. The prompt carries a worked example I wrote for Lodhi Garden: the tomb from the bottom of its steps, the old trees, a squirrel on a trunk, the Athpula bridge.
- Fallback sketch. A free SVG, drawn from the geometry, shows instantly while the real drawings load.
- Save everything to MongoDB Atlas.
The walk guide is a Mastra agent with tools for "nearest unheard place" and "nearest planned shot". Gemma 4 runs behind an OpenAI-compatible endpoint, with Gemma 3 as an automatic Mastra fallback model. If both are down, every field falls back to a template built from the same facts, so the app never just breaks.
The drawings: Gemma looks, FLUX draws
Gemma looks at the real Commons photo and describes the shot to aim for, including what the place actually looks like: "the massive stone dome of Bara Gumbad viewed from a low angle". FLUX.1 [schnell], an open-weight Apache-2.0 image model, draws it in four steps. Every prompt starts with "A young child's wax crayon drawing…" on purpose: a crayon sketch says "rough idea of the shot", where a photoreal render would pretend to be a prediction.
MongoDB Atlas is what makes the walk work
- Two $geoNear questions on every walk tick. "Which planned shot that I haven't done yet is nearest?" uses a 2dsphere index on the stand points. "Which documented place within 350 m haven't I heard about yet?" uses a second geo index over Wikipedia places.
- GridFS as a "never make it twice" store. Every drawing and voice clip is stored under a fingerprint of what produced it: model and prompt, or voice and exact sentence. A second walk past the same tomb costs nothing.
- A time-series collection for the route. It feeds the walk diary: your route on a map, distance, stories heard, planned shots reached, and every clip again.
- A TTL cache for the free APIs. Places for 30 days, photos for a week, weather for an hour, so OpenStreetMap and Wikimedia aren't asked the same thing twice.
ElevenLabs is the voice in your ear
Text written for a screen sounds wrong spoken, so before anything goes to ElevenLabs, "f/8" becomes "f 8" and "1/250s" becomes "a 250th of a second". Each sentence is spoken with eleven_v4 as a small MP3 suited to earbuds, and cached in GridFS. Phone browsers only play sound after a tap, so the Start button plays a tiny silent WAV to unlock audio for later. Pocket mode is a black overlay with the Screen Wake Lock API holding the screen on, because phone browsers pause GPS when the screen locks.
Stack
| Part | What |
|---|---|
| Open data | OpenStreetMap (Nominatim, Overpass), Wikipedia, Wikimedia Commons, Open-Meteo / MET Norway, suncalc |
| Language + vision | Gemma 4 26B-A4B (Gemma 3 12B fallback) on Cloudflare Workers AI's free tier |
| Drawings | FLUX.1 [schnell] on Cloudflare Workers AI |
| Agent framework | Mastra (workflow, agents, tools, model fallback) |
| Database | MongoDB Atlas (free M0): geospatial, GridFS, time series, TTL |
| Voice | ElevenLabs eleven_v4, with the phone's own voice as fallback |
| App | Next.js 16 + TypeScript, mobile-first, Leaflet + OpenStreetMap tiles, hosted on Render |
I built this over a few late nights with an AI coding agent, Claude Code, and installed the official Mastra, MongoDB and ElevenLabs agent skills first, so the API calls matched current docs. My job was deciding what LumiRoam should be. A few of the best parts came from me pushing back on the agent's first idea, like the drawings and the history.
What Went Wrong
- The sun was 106° below the horizon. That's impossible: the most it can be is 90. suncalc's version 2 quietly switched from radians to degrees, and the TypeScript types still described version 1. The first golden-hour test would have sent me out at midnight.
- "Lodhi Garden" was a tomb. OpenStreetMap's geocoder matched "Tomb of Sikandar Lodhi, Lodhi Garden…", because the garden is part of the tomb's address, and the park itself is mapped as "Lodhi Gard*ens*". My walk was centred on the garden's edge and wandered off to the market next door. Then my place classifier, which only knew English words, threw away "Bara Gumbad" and "Shish Gumbad". Now it prefers areas, keeps the walk inside the garden, and knows gumbad, masjid, qila, maqbara, baoli.
- Gemma 4 thinks before it talks. A 50-word story took 40 seconds, mostly hidden reasoning. My walk timeout cut it off, so the guide quietly read raw Wikipedia instead. Switching thinking off brought it to about 2 seconds, at a quarter of the free credits.
Why Does Open Innovation Matter?
Because the knowledge is shared. OpenStreetMap, Wikipedia and Wikimedia Commons are the reason LumiRoam exists. They're built by volunteers and open to anyone, so a beginner can build a guide on top of them, and every fact it tells you can be traced back to a page anyone can read and fix. A closed map or a paid photo library would have meant keys, contracts and limits on what I could retell. Open data is what makes this kind of project possible for everyone, not just for companies.
Open weights meant my infrastructure could fail three times without the app failing once. I planned to run Gemma on my own GPU server, but that needs a payment card I couldn't use. Then my Hugging Face credits ran out. Then the free ZeroGPU allowance said no after a single drawing. I didn't rewrite anything: Gemma 4 and FLUX.1 [schnell] are open models served by many hosts behind the same kind of API, so each move was a change to one environment variable. Today both run on Cloudflare's free tier. With a closed model, its owner's API is the only door, and when it closes, so does your app.
It can run on a server I own. That was the original plan, and it's still one setting away: the same code talks to Ollama or vLLM on my own GPU. Even on a hosted model today, Gemma never receives your GPS coordinates, only place names and directions like "about 200 metres on your left".
It costs nothing per call. The whole AI stack runs on free tiers, and caching every drawing and voice clip in MongoDB keeps it that way. The one closed piece is the ElevenLabs voice, and if it disappears, the phone's built-in voice reads the same text.
Taking It Outside
These are my own photos from Lodhi Garden, the place I planned the walk for: the shaded paths under the old trees, a squirrel sitting on a root, and the tombs.
!A shaded path through the garden, ivy on the left and old trees on the right
!A squirrel on a big tree root in low sun
!A domed tomb in warm light, framed by a branch
!The corner of a domed tomb shot from a low angle, in black and white
The last one is the kind of shot the plan asks for. For the second stop it suggested "get low to the ground and aim upward to make the dome feel massive and imposing", and drew a dome seen from below. A low angle and a clear sky is what makes that corner look as heavy as it does.
My Agent Session
I built LumiRoam with Claude Code. The session shows the whole messy middle: the moves between three model hosts, the bugs above and many more, and where I steered it. Secrets, emails and local paths are scrubbed, and the messages where I shared credentials are shown without them.
{% agent_session 724 0..144 %}
The Lodhi Garden part on its own: the tomb that wasn't a garden, Gemma's 40-second thinking, and the drawings coming out right.
{% agent_session 724 110..135 %}
Prize Categories
- Best Use of Gemma: Gemma 4 is the one model that both looks (it reads four Commons photos per plan to describe each place accurately) and writes (plans, Wikipedia-grounded history, the walk stories), with its thinking switched off for speed.
- Best Use of Mastra: a five-step planning workflow, two agents (planner and walk guide), tools for places, sun and light, weather, and the database, model fallback, and schema-checked JSON output (the schema goes in the prompt, so it works on any host).
- Best Use of MongoDB Atlas: $geoNear on two 2dsphere indexes on every walk tick, GridFS as a "never generate twice" store for drawings and voice, a time-series collection for routes, and TTL caching for the free APIs.
- Best Use of ElevenLabs: the whole walk is audio-first, with speech-friendly settings text, one cached eleven_v4 clip per sentence, and replay in the walk diary.
- Best Use of Render: LumiRoam is a Render web service at lumiroam.onrender.com. The hosting is not decoration: phone GPS and the Screen Wake Lock API only work over HTTPS, and a real URL is what lets anyone open the app on a phone and walk with it.
Map data © OpenStreetMap contributors (ODbL). Text from Wikipedia (CC BY-SA). Reference photos from Wikimedia Commons, credited to each photographer in the app.
1 reaction
Mentions
@interrailinfosvenska an extract of all the coffee shop names, locations and opening times on the planet in OSM is probably only a few MB? i have no idea how these serverless thingies work so i would probably do it clientside, bake a kdbush at build time mourner/geokdbush
Daily Dose of Nature: a private, offline-first nature mission just for you --- title: "Daily Dose of Nature: a private, offline-first nature mission just for you" published: true tags: devchallenge, hf26challenge cover_image: dev-to-uploads.s3.us-east-2.amazonaws.com --- This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass What I Built Daily Dose of Nature is an installable, phone-first web app that answers one question: what is worth seeing around me right now, in the time I have? I started it on October 7 for this challenge. You pick how long you have (a 15-minute break to an afternoon) and where you are. The app works out how far you can walk and back, looks at the season, the time of day and the weather, and gives you one mission: a named park, garden, wood or waterside spot within reach, plus a short list of plants, fungi, birds and other animals that people have actually recorded around there at this time of year. The briefing tells you the direction and distance in plain words, then tells you to put the phone away. The best spot doesn't have to be where you start. The app looks at everything inside your walking radius and sends you to the reachable place with the strongest observed species. You can find things on the way, too. It is for people who would like to go outside but have no plan, no energy, or bad weather as an excuse. For now, it covers only Berlin to optimise the data as much as possible. Demo Live app: dailydoseofnature.onrender.com Code {% embed kerstinSimoneM/dailydoseofnature %} MIT licensed. Data and image licences are in the README and in the app's About page. How I Built It Data, built once and shipped with the app. A Python pipeline in /pipeline turns open data into a small static pack: - Species and sightings: 30,850 filtered iNaturalist research-grade observations from a GBIF download (DOI), covering 230 taxa (110 plants, 78 invertebrates, 22 fungi and lichens, 17 birds, and a few mammals and reptiles). It is a download, not a scrape of the API. Only CC0 and CC BY records are accepted. - Where to go: 7 hand-picked destinations plus about 500 named parks, gardens, woods and waterside spots from OpenStreetMap, merged and de-duplicated by a second script. - Weather: Open-Meteo forecasts, used for safety rules. Citizen-science data records where people look, not only where species are. The pack therefore stores aggregate rates per H3 hex cell, shrunk towards a baseline when a cell has few observations, and it flags low-effort cells. It contains no exact occurrence coordinates. On the phone, everything local. TypeScript, React, Vite and a PWA service worker, with no backend, analytics or account. Mission generation is deterministic: 1. Turn your time budget into a walking radius (60% of the outing is walking, with a detour factor). 2. Keep only public destinations whose round trip fits the budget. 3. Apply safety rules: only street trees in a storm, only lit places on ice or after dark, only shaded spots in heat. 4. Among the reachable places, choose the one whose surroundings have the strongest observed species for this season and time of day. 5. Rank the species for that place. Directions without GPS. The briefing gives the compass direction. While the sun is up, SunCalc adds a hint computed on the device, such as "keep the sun on your right". After dark, the hint is dropped. Open-weight model, optional. An English-only experiment runs SmolLM2-360M-Instruct (Apache-2.0) through WebLLM and WebGPU, entirely on the device. It sees only the season, weather category, time budget and candidate species IDs, never coordinates or place names. Its output is validated against the supplied candidates, and it can't override safety guidance. It is a roughly 200 MB download that starts only when the user taps "Try optional AI mission". An honest result. In my browser tests the model did not clearly beat the handcrafted briefing. Its objectives didn't adapt reliably to the outing length or add real ecological relationships. So the deterministic mission is the default and the AI stays opt-in, until the data has richer habitat and ecology information. The README says the same. Privacy by design. Location is requested only when you tap "Use my location", is used in memory for ranking, and is never sent anywhere. The forecast always uses a fixed Berlin-centre point, so no request contains your position. There are no map tiles, and a strict CSP limits what the app may contact. Checklist entries store only a species ID and a date. Photos and credits. Species photos come from iNaturalist and Wikimedia Commons. They are for non-commercial use only, and each one is credited in the app with its author and licence (CC BY, CC BY-SA, CC BY-NC variants, or public domain). A pipeline script (prepare_species_images.py) rebuilds the credits file from a source table, so a photo can't ship without its attribution. Where I resized a photo, the credit says so. Testing. 56 unit tests (engine, briefing, data pack), 26 Python pipeline tests, and a Playwright end-to-end test that completes a mission. A regression test covers the "no destination fits this outing" bug, which came from having only 7 destinations: at the default 40 minutes only 1 of 28 start areas had a place in reach. With the OpenStreetMap places, 25 of 28 do. Known limitations. - Suggestions summarise past observations. They are not live sightings, calibrated probabilities or guarantees. - Observer bias is reduced, not removed. - After dark, or in storm, ice or heat, the choice falls back to the 7 hand-picked places, because the OpenStreetMap ones have no lighting or shade data. - Added to the iOS Home Screen, location access can be blocked by a platform quirk. The area picker is the workaround. - Berlin only. I took it outside On Sunday evening, I wanted to finish the app first, so I went late, but I made it outside! With 40 minutes, the app suggested a nearby small park. With a short 10-15 minute break, it put noticing the nature around me ahead of walking somewhere. It was already dark, so I didn't see any of the species it suggested. I did smell wet leaves on the ground and hear a bird singing, though only briefly. The app hadn't warned me about the dark, because it only counted "night" an hour after sunset. That's fixed now, and it shows a safety note as soon as part of the outing is after dark. Why Does Open Innovation Matter? - Your location stays yours. A cloud API would need your position to give a local answer. Here the ranking runs on the phone, and you can check that in the open code. - It costs nothing to run. It is a static site with no server, key or per-request bill. Open data (GBIF/iNaturalist, OpenStreetMap, Open-Meteo, SunCalc) feeds it, and the sources are credited in the app. - Open data made the fix possible. When the app kept saying "no destination fits", OpenStreetMap's open licence let me add about 500 real places in one afternoon, and coverage went from 1 of 28 start areas to 25 of 28. - The model is swappable and optional. The app works without it. Because the weights are open, I could pin an exact revision, check its licence, run it on the device and judge it against the baseline. That is how I found it didn't help yet. - Anyone can rebuild it. The pipeline can be pointed at another city, and every recommendation can be traced to the data. Prize Categories - Best Use of Render: the live app at dailydoseofnature.onrender.com is a Render Static Site. Render builds it with npm ci && npm run build and serves the PWA and its data pack over HTTPS, so the app needs no server of its own. - Best Use of GitHub Copilot: I built this project with the GitHub Copilot agent in VS Code, in a chat session working directly in the repo. One example: when users kept seeing "no destination fits this outing", Copilot checked the real data and found that only 1 of 28 start areas had a destination in reach at the default 40 minutes, because the app knew just 7 places. It wrote a pipeline that pulled about 500 named parks, woods and waterside spots from OpenStreetMap, then added a regression test. Coverage went to 25 of 28 areas. Copilot also helped add the on-device sun-direction hint, and it kept the 56 unit tests, the Python tests and the end-to-end test green as I changed things.
LumiRoam: the tour guide for beginner photographers This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass What I Built I'm a beginner photographer. I have an entry-level Canon, I know roughly what aperture and shutter speed do, and I've lost count of the times I've stood in front of something beautiful at the wrong hour, from the wrong side, with no idea where to start. Every AI tour guide I tried would happily tell me about the building. None of them helped me learn how to photograph it. LumiRoam is a tour guide for beginner photographers. You tell it where you're going, when, and what camera you carry. It plans a short photowalk of four stops, and for each one it gives you: - where to stand, often the exact spot where someone took a photo now on Wikimedia Commons - which way to face, and whether the light will be in front of you, beside you or behind you, which is the thing I most often get wrong - a focal length that fits your camera and your lenses ("about 34mm on your 18-55", or "wider than your 18mm, step back 13 m") - a starting point for your settings, with the reason behind it, so you know what to change when the light changes - a little history of the place, told only from its Wikipedia article - one child-like crayon drawing of the shot to aim for It doesn't take the photo for you, and it isn't meant to. I don't want an app that hands me the "perfect settings", because then I'd never learn to see light myself. So the settings are a place to start, not a recipe: the plan explains why ("the sun is behind you, so the front is evenly lit"), and the tip is about composition ("get low and aim up to make the dome feel massive"). After the first frame it's on you to look at the back of the camera and adjust. The drawing is deliberately rough, a crayon sketch of the idea rather than a picture to copy. Then you put the phone in your pocket. That's the "touch grass" part. Every 75 metres, or every three minutes, LumiRoam speaks into your earbuds: > "Your planned shot of Bara Gumbad is about 200 metres straight ahead, to the south." > "You're at your planned spot for Sikandar Lodhi's grave. Sikandar Khan Lodi was the Sultan of the Delhi Sultanate from 1489 to 1517… Face south-east, that's on your left. Try a low angle, looking up at the structure to make it feel more imposing." In between it tells you short stories about what you're walking past. The screen gets about a minute, at the start. The rest is the walk, and the camera. Demo {% embed youtu.be %} Live app: lumiroam.onrender.com (free tier: if it's been asleep, the first load takes about half a minute) The screens below are from the live app, planning Lodhi Garden for 17:30 with a Canon EOS 250D: !The plan: sunset time, golden hour window, and four numbered stops on the map !A stop card: drawing, history, where to stand, lens, settings, tip !The walk diary: route on the map, distance, stories heard, and a replayable clip for each one Here's what a real plan for Lodhi Garden, New Delhi, at 17:30 came back with: | Stop | The drawing Gemma asked for | History (from Wikipedia only) | |---|---|---| | Lodhi Gardens | "Light through leaves": an old tree with low sun through its canopy | A 90-acre city park full of Sayyid and Lodi dynasty monuments | | Bara Gumbad | "Dome from below": the great dome from a low angle against an orange sky | Built in 1490 CE, with the earliest full dome in Delhi | | Shish Gumbad | An upward detail of its arches | The "glazed dome", a Lodi-dynasty tomb from around 1494 | | Sikandar Lodi's tomb | The tomb as a silhouette against the sunset | Sikandar Lodi ruled the Delhi Sultanate from 1489 to 1517 | Code {% github stealth-raptor/LumiRoam %} How I Built It Built on the commons Almost everything LumiRoam knows comes from people who shared their work with everyone: - OpenStreetMap volunteers mapped the tombs, paths, parks and viewpoints. That's how the app knows what's physically there, and the map you see is theirs too. - Wikipedia editors wrote the history. The guide is only allowed to retell that text, never to add a name or date of its own, so every story can be checked. - Wikimedia Commons photographers uploaded their photos under free licences, and many recorded where they stood. That's how LumiRoam can say "stand here" and mean a spot a real person already proved. Every reference photo is credited to its photographer in the app. None of these needs a key, a contract or a fee. That's what made a project like this possible for a beginner in a few nights. The one rule: code computes, Gemma explains A language model that says "use f/8 and stand to the south" sounds confident whether or not it's right. So everything that can be calculated is calculated by plain code, and Gemma only explains it: - The sun. suncalc gives the sun's height and direction at each stop for that exact moment. That names the light: blue hour, golden hour, low sun, high sun. - Where to stand. If a geotagged Commons photo records where its camera was, 30 to 300 metres from the subject, you stand there. Otherwise the app picks a spot 45° off the sun, at a distance that fits the subject's size. - Which lens. From the subject's size and your distance, it works out a focal length that fits, converts it for your sensor (1.6× for Canon APS-C, 1.5× for other APS-C), and checks it against the lenses you own. - A starting exposure. The old sunny-16 rule as code: a base brightness for the light, minus the cloud forecast. It's a first guess for you to correct, not an answer. All of that goes into a fact sheet for each stop. Gemma's job is to turn it into friendly sentences, explain the reasons, write the history from the Wikipedia text, and look at the photos. I learned why this rule matters the hard way: Gemma once "improved" my computed lens advice into "zoom out to 15mm" on a lens that physically stops at 18. Since then, the lens line is shown exactly as computed. Mastra runs it as a workflow, not a free-roaming agent Planning is a Mastra workflow with five fixed steps: locate → find spots → write plans → fallback sketch → save A smaller open model left to decide by itself which of six tools to call is unreliable. A workflow keeps the order fixed and puts Gemma where it's strong. - Locate. OpenStreetMap's Nominatim finds the place. The time zone is looked up offline from the coordinates, so the sun maths never depends on an API. Weather comes from Open-Meteo, with MET Norway as a backup. - Find spots. OpenStreetMap says what's physically there. Wikipedia says what's notable, and how many people read about it in the last 30 days. That popularity signal alone turned my London test from "a medieval postern and a priory ruin" into "Tower Bridge, the Monument, HMS Belfast". - Write plans. All four stops go to Gemma 4 in a single call: four Commons photos plus four fact sheets. Because it sees the whole walk at once, it can make the four drawings different from each other. The prompt carries a worked example I wrote for Lodhi Garden: the tomb from the bottom of its steps, the old trees, a squirrel on a trunk, the Athpula bridge. - Fallback sketch. A free SVG, drawn from the geometry, shows instantly while the real drawings load. - Save everything to MongoDB Atlas. The walk guide is a Mastra agent with tools for "nearest unheard place" and "nearest planned shot". Gemma 4 runs behind an OpenAI-compatible endpoint, with Gemma 3 as an automatic Mastra fallback model. If both are down, every field falls back to a template built from the same facts, so the app never just breaks. The drawings: Gemma looks, FLUX draws Gemma looks at the real Commons photo and describes the shot to aim for, including what the place actually looks like: "the massive stone dome of Bara Gumbad viewed from a low angle". FLUX.1 [schnell], an open-weight Apache-2.0 image model, draws it in four steps. Every prompt starts with "A young child's wax crayon drawing…" on purpose: a crayon sketch says "rough idea of the shot", where a photoreal render would pretend to be a prediction. MongoDB Atlas is what makes the walk work - Two $geoNear questions on every walk tick. "Which planned shot that I haven't done yet is nearest?" uses a 2dsphere index on the stand points. "Which documented place within 350 m haven't I heard about yet?" uses a second geo index over Wikipedia places. - GridFS as a "never make it twice" store. Every drawing and voice clip is stored under a fingerprint of what produced it: model and prompt, or voice and exact sentence. A second walk past the same tomb costs nothing. - A time-series collection for the route. It feeds the walk diary: your route on a map, distance, stories heard, planned shots reached, and every clip again. - A TTL cache for the free APIs. Places for 30 days, photos for a week, weather for an hour, so OpenStreetMap and Wikimedia aren't asked the same thing twice. ElevenLabs is the voice in your ear Text written for a screen sounds wrong spoken, so before anything goes to ElevenLabs, "f/8" becomes "f 8" and "1/250s" becomes "a 250th of a second". Each sentence is spoken with eleven_v4 as a small MP3 suited to earbuds, and cached in GridFS. Phone browsers only play sound after a tap, so the Start button plays a tiny silent WAV to unlock audio for later. Pocket mode is a black overlay with the Screen Wake Lock API holding the screen on, because phone browsers pause GPS when the screen locks. Stack | Part | What | |---|---| | Open data | OpenStreetMap (Nominatim, Overpass), Wikipedia, Wikimedia Commons, Open-Meteo / MET Norway, suncalc | | Language + vision | Gemma 4 26B-A4B (Gemma 3 12B fallback) on Cloudflare Workers AI's free tier | | Drawings | FLUX.1 [schnell] on Cloudflare Workers AI | | Agent framework | Mastra (workflow, agents, tools, model fallback) | | Database | MongoDB Atlas (free M0): geospatial, GridFS, time series, TTL | | Voice | ElevenLabs eleven_v4, with the phone's own voice as fallback | | App | Next.js 16 + TypeScript, mobile-first, Leaflet + OpenStreetMap tiles, hosted on Render | I built this over a few late nights with an AI coding agent, Claude Code, and installed the official Mastra, MongoDB and ElevenLabs agent skills first, so the API calls matched current docs. My job was deciding what LumiRoam should be. A few of the best parts came from me pushing back on the agent's first idea, like the drawings and the history. What Went Wrong - The sun was 106° below the horizon. That's impossible: the most it can be is 90. suncalc's version 2 quietly switched from radians to degrees, and the TypeScript types still described version 1. The first golden-hour test would have sent me out at midnight. - "Lodhi Garden" was a tomb. OpenStreetMap's geocoder matched "Tomb of Sikandar Lodhi, Lodhi Garden…", because the garden is part of the tomb's address, and the park itself is mapped as "Lodhi Gard*ens*". My walk was centred on the garden's edge and wandered off to the market next door. Then my place classifier, which only knew English words, threw away "Bara Gumbad" and "Shish Gumbad". Now it prefers areas, keeps the walk inside the garden, and knows gumbad, masjid, qila, maqbara, baoli. - Gemma 4 thinks before it talks. A 50-word story took 40 seconds, mostly hidden reasoning. My walk timeout cut it off, so the guide quietly read raw Wikipedia instead. Switching thinking off brought it to about 2 seconds, at a quarter of the free credits. Why Does Open Innovation Matter? Because the knowledge is shared. OpenStreetMap, Wikipedia and Wikimedia Commons are the reason LumiRoam exists. They're built by volunteers and open to anyone, so a beginner can build a guide on top of them, and every fact it tells you can be traced back to a page anyone can read and fix. A closed map or a paid photo library would have meant keys, contracts and limits on what I could retell. Open data is what makes this kind of project possible for everyone, not just for companies. Open weights meant my infrastructure could fail three times without the app failing once. I planned to run Gemma on my own GPU server, but that needs a payment card I couldn't use. Then my Hugging Face credits ran out. Then the free ZeroGPU allowance said no after a single drawing. I didn't rewrite anything: Gemma 4 and FLUX.1 [schnell] are open models served by many hosts behind the same kind of API, so each move was a change to one environment variable. Today both run on Cloudflare's free tier. With a closed model, its owner's API is the only door, and when it closes, so does your app. It can run on a server I own. That was the original plan, and it's still one setting away: the same code talks to Ollama or vLLM on my own GPU. Even on a hosted model today, Gemma never receives your GPS coordinates, only place names and directions like "about 200 metres on your left". It costs nothing per call. The whole AI stack runs on free tiers, and caching every drawing and voice clip in MongoDB keeps it that way. The one closed piece is the ElevenLabs voice, and if it disappears, the phone's built-in voice reads the same text. Taking It Outside These are my own photos from Lodhi Garden, the place I planned the walk for: the shaded paths under the old trees, a squirrel sitting on a root, and the tombs. !A shaded path through the garden, ivy on the left and old trees on the right !A squirrel on a big tree root in low sun !A domed tomb in warm light, framed by a branch !The corner of a domed tomb shot from a low angle, in black and white The last one is the kind of shot the plan asks for. For the second stop it suggested "get low to the ground and aim upward to make the dome feel massive and imposing", and drew a dome seen from below. A low angle and a clear sky is what makes that corner look as heavy as it does. My Agent Session I built LumiRoam with Claude Code. The session shows the whole messy middle: the moves between three model hosts, the bugs above and many more, and where I steered it. Secrets, emails and local paths are scrubbed, and the messages where I shared credentials are shown without them. {% agent_session 724 0..144 %} The Lodhi Garden part on its own: the tomb that wasn't a garden, Gemma's 40-second thinking, and the drawings coming out right. {% agent_session 724 110..135 %} Prize Categories - Best Use of Gemma: Gemma 4 is the one model that both looks (it reads four Commons photos per plan to describe each place accurately) and writes (plans, Wikipedia-grounded history, the walk stories), with its thinking switched off for speed. - Best Use of Mastra: a five-step planning workflow, two agents (planner and walk guide), tools for places, sun and light, weather, and the database, model fallback, and schema-checked JSON output (the schema goes in the prompt, so it works on any host). - Best Use of MongoDB Atlas: $geoNear on two 2dsphere indexes on every walk tick, GridFS as a "never generate twice" store for drawings and voice, a time-series collection for routes, and TTL caching for the free APIs. - Best Use of ElevenLabs: the whole walk is audio-first, with speech-friendly settings text, one cached eleven_v4 clip per sentence, and replay in the walk diary. - Best Use of Render: LumiRoam is a Render web service at lumiroam.onrender.com. The hosting is not decoration: phone GPS and the Screen Wake Lock API only work over HTTPS, and a real URL is what lets anyone open the app on a phone and walk with it. Map data © OpenStreetMap contributors (ODbL). Text from Wikipedia (CC BY-SA). Reference photos from Wikimedia Commons, credited to each photographer in the app.