Every "touch grass" app assumes you can walk. Mine asks how far you can go today. A developer built Doorstep, an open-source accessibility-focused walking app that takes a user's daily physical distance limit as input, filters nearby OpenStreetMap places in code to those within that ceiling, and uses a locally-run Gemma 3 4B model via Ollama to pick one destination worth the walk. The app deliberately keeps distance as arithmetic rather than a model judgement call, and the public demo runs on a park in a city the author does not live in to avoid exposing their home location. This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass https://dev.to/challenges/hacktoberfest-week1-2026-10-05 I read the other entries to this challenge before I wrote a line. Around ninety of them. Quest generators, trail guides, bird call identifiers, foliage route planners, apps that lock your phone until you go outside. Every single one assumes the thing stopping you is motivation. None of them are for someone who already wants to go out and cannot. Someone three weeks after surgery. Someone with a knee that decides each morning how far it is willing to travel. Someone eighty years old who can manage the end of the street and back, and some days not that. Doorstep asks the one question none of the others ask: How far today? Not a target. A ceiling. Some days it is smaller, and that is allowed. It takes today's number, finds everything genuinely inside it, and asks a local open-weight model to pick one place worth the walk. How far today? =====o---------------- 200 metres Balbhim Mandir 138 metres · somewhere to sit · shaded "Shaded most of the way, and somewhere to sit at the end." Then, later, one question: did you go? That is the entire loop. No steps, no pace, no calories, no streak. Doorstep holds two things about you: how far your body could carry you today, and the exact point those metres are counted from. Together, that is a medical record with a home address stapled to it. Which creates a problem for a demo. The app displays place names, not coordinates. "The mandir at a hundred and thirty metres." "The cafe at the end of the road." A screenshot of this working where I live gives away where I live, to within a couple of streets, without a single number on screen. So this demo runs on a public park in a city I do not live in, with a few days of history written in by hand so the panel is not empty. The app is real, the OpenStreetMap data is real, every sentence Gemma writes is real. The address is the one thing that is not. The second screen is the one I am proudest of. Pull the slider below the nearest mapped place and Doorstep does not stretch, improvise, or round down. It says so. A tool that asks for your address should be able to demonstrate itself without publishing its author's. Outside, for people whose outside is two hundred metres. Every app that wants to get you outdoors assumes the only thing stopping you is motivation. Doorstep assumes the opposite: that you already want to go, and the limit is your body. It asks one question nothing else asks. Built for the Hacktoberfest Open-Source AI Challenge, Week 1: Touch Grass https://dev.to/challenges/hacktoberfest-week1-2026-10-05 . Fitness apps treat distance as a number to beat. Streaks, personal bests, "you walked less this week". For someone recovering from surgery, living with chronic pain, or simply eighty years old, distance is not an ambition. It is a ceiling set by the body, and it moves day to day. Doorstep takes today's ceiling as an input, finds everything genuinely within it, and asks a local open-weight model to pick… Gemma 3 4B through Ollama, running locally. OpenStreetMap for the neighbourhood, downloaded exactly once. Node and a single HTML page. The daemon binds to loopback. The model chooses which. It never chooses how far. Every place is filtered against today's reach in code, before the model is asked anything: js const eligible = places.filter p = p.metres <= reach ; By the time Gemma sees the list, every option on it is already safe. The worst it can do is pick a dull one. This is not architectural tidiness. A language model that cheerfully offers somewhere "just a little further" is, for this user, an app that strands them two streets from home with nowhere to sit. Distance is the one thing in this product that must not be a judgement call, so it is arithmetic. A pick naming a place that was not on the list is treated as a hallucination rather than a suggestion, and falls back to the nearest place to sit. There is nothing mapped within 100 metres of you today. That is the map being thin here, not you. It does not quietly widen the radius to find something. It does not invent a bench. For this user a made-up destination is worse than no answer, because they will walk to it. I fetched the map twice, for two Indian cities about 300 km apart. | | The smaller city | The larger city | |---|---|---| | places within 1200m | 28 | 701 | | benches | 0 | 24 | | mapped trees | 3 | 566 | | anything within 200m | nothing | 3 places | Same code, same radius, same query. In the larger one, Doorstep has a mandir, a cafe and five hundred mapped trees to choose between. In the smaller one it has to say there is nothing within reach of your door , not because the streets are empty, but because OpenStreetMap does not know about them. My first instinct after that first fetch was to write "India doesn't have mapped benches". The second fetch showed that was wrong, and the real answer is more interesting. OSM is volunteer-mapped, so the street furniture layer tracks where mappers live , not where benches are. For an app whose entire promise is "there is somewhere to sit 200 metres away", that means the data is thinnest in exactly the places least likely to have someone else's app built for them. So the list of what counts as a destination is not the list you would write looking at a European map: | Mapped everywhere here | Mapped almost nowhere here | |---|---| | temples and mandirs: shaded, quiet, you may sit | benches | | parks, gardens, maidans | drinking fountains | | markets, chowks, corner shops | individual street trees | Both lists are in the query. Where a bench exists it is the best possible answer. Where one does not, a temple courtyard at seven in the morning is a real place to sit down, and it is on the map. The map comes from Overpass, a free service run by volunteers, so I wrote a loop that tries several mirrors. The first version destructured the response like this: { elements = } = await r.json ; That default is a trap. A mirror answering HTTP 200 with no data would have produced an empty array, which is truthy, which counted as success, and Doorstep would have silently saved an empty map . The user-visible result of that bug: an app that tells someone there is nowhere within reach of their own front door. The single most discouraging thing this project could possibly say, caused by one = . It now treats a 200 with no elements as a failed mirror and moves on. Worth diagnosing properly too: kumi.systems returns 500, overpass.osm.ch returns HTML with a 200 status, and only overpass-api.de actually answers. Streaks punish bad days. Here, bad days are not a failure of will, they are the condition being lived with. A streak counter tells someone whose body took a week off that they broke something. So it counts days you got out across a month, which one hard week cannot erase, and the average reach on the days you went , never the days you did not. The model is also instructed never to mention distance, pace, progress, trying harder, or the person's health back at them. It is told to write like a friend who noticed something, not a coach. Three reasons, and the first one decides it on its own. 1. This is health data, and it is stapled to your address. An app that knows you managed 150 metres today and 400 yesterday is keeping a record of your body getting better or worse. Doorstep also knows the exact point all of those metres are measured from, because it has to. A medical record with a slider on it, and a home address, in the same file. I noticed how strongly I felt this while building it. Every screenshot in this post is centred on a public park, not my front door, and data/ is in .gitignore so the real map never reaches the repo. I did that instinctively, for a demo, with made-up history in it. Then I realised that instinct is the argument. If I will not publish those two numbers together for a hackathon post, I certainly should not be posting them to someone's API every morning on behalf of a person who cannot walk far. There is no network call here to audit, because after setup there is no network call. 2. It has to work with the radio off. The map downloads once and never again. After that Doorstep works in a basement flat, on a dead prepaid SIM, in a lane with no signal. A closed API makes the one moment it is needed, someone standing at their door deciding whether to risk it, depend on a connection. 3. It costs nothing per answer. A service that bills per request is a service a pensioner switches off. And a fourth I did not expect. Building this on a small local model forced the distance logic out of the model entirely, because I could not trust a 4B model with a number where being wrong hurts someone. A frontier API would have been convincing enough that I probably would have let it decide, and shipped something more dangerous. Having no escape hatch produced the safer design. Gemma 3 4B does the one job it is genuinely good at here: reading a short list of real places and saying which one is worth today, while every decision that could hurt someone stays in code. It runs on a laptop CPU, which is the only reason an app holding somebody's mobility record can stay on their own machine.