DEV Community

XExpertAI
XExpertAI

Posted on

Outside Hour: a local Gemma model that wants you off your phone in 60 seconds

Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass Submission 🌿

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass

What I Built

Most "get outside" apps want one more scroll. Outside Hour gives itself 60 seconds of your attention, and
shows it: a ring in the corner counts down while the page is on your screen. You tell it where you are and how
long you have. A Gemma model running on my laptop picks one real park, lake or garden nearby and writes a pocket
card. When the minute is up, the ring turns red, the page dims and asks you to put the phone down. Then the
screen goes black, and the biggest thing on it is your turn-back time: 07:40.

Use it the evening before: plan tomorrow's 7 a.m. walk tonight, so the morning needs no screen at all.

The card has five things:

  • Where to go: one real place within walking distance, from OpenStreetMap, and what is mapped there (Lotus Pond and Bamboo Island Pond inside Cubbon Park, 66 mapped trees, 89 benches).
  • Which way: a compass arrow with the bearing and the straight-line distance, worked out in code from the coordinates and labelled "straight line, not a route". No map tiles, no map service.
  • When to turn back: leave, turn-back and home times, cut short so you're back before sunset.
  • Three things to notice at that place in that light: the water surface at Lotus Pond, dew and long shadows at 7 a.m., the sky changing before sunset.
  • What to bring for the forecast: rain, UV, heat, wind, air quality, a torch after dark.

Then three ways off the screen:

  1. Phone down: a black screen with the place, the direction and a huge turn-back time, plus two meters: time on this screen and time away from it.
  2. Print pocket card: an A6-wide card for your pocket, so the phone can stay home.
  3. Turn-back alarm (.ics): a calendar event whose alarm goes off at turn-back time. The phone's own clock does the reminding, so the app can be closed.

The meters are honest about what a web page can know. They use the browser's Page Visibility API: time counts
only while the page is visible, and "away" is the time it has been hidden since you tapped Phone down. It can't see
other apps, and the screen says so.

One rule shaped the design: anything that could hurt you if it were wrong (the place, walking times, turn-back
time, direction, sunset, weather) comes from code and open data. Gemma only chooses between real places and
writes the words.

Demo

Plan Pocket card Phone down Printed card
Form with the 60-second ring Pocket card for Cubbon Park Phone-down screen Printable pocket card

Real run on my laptop CPU, no GPU, at phone size. The start is MG Road metro station in Bengaluru, a public place
I used as a demo location; the plan is for 60 minutes at 07:00 the next morning, because I made it at night.
Places, times and weather are real OpenStreetMap and Open-Meteo data from 11 October 2026. In the phone-down
screenshot, the 20 seconds "away" come from a simulated phone lock in my recording script (headless browsers never
hide a page); on a real phone, locking the screen does the same thing.

Outside Hour: pocket card, the 60-second ring running out, phone down

  • Demo video (81 s, voiceover): YouTube (also in the repo as outside-hour-demo-v2.mp4). The same real phone-size run, framed and zoomed: the 29 s model wait is cut, a few moments are held so you can read them, and the simulated-lock "away" part runs at 4Ă— speed. All three are labelled on screen. A second real run (Lalbagh, 45 min, 17:30) closes it.
  • Earlier desktop demo: 51 s, captioned and 73 s, uncut real time.

Code

GitHub logo xexpertai / outside-hour

Outside Hour: one honest plan to get outside, written by a local Gemma model on your laptop. DEV Hacktoberfest 2026 Week 1.

Outside Hour

A pocket card for one short trip outside, from a local Gemma model, on a page that gives itself 60 seconds of your attention.

Outside Hour looks up real parks, lakes, gardens and viewpoints near you on OpenStreetMap, and what is mapped inside them (named ponds, trees, benches, drinking-water taps). It checks the weather, sunset and air quality on Open-Meteo, and asks Gemma 4 E2B, an open-weight model running on your own CPU, to pick one place and write a short pocket card: where to go, when to turn back, three things to notice there, and what to bring. Then it gets out of the way: a black "phone down" screen, a printable card, and a calendar alarm for the turn-back time.

Built for the DEV Ă— Hacktoberfest 2026 Open-Source AI Challenge, Week 1: "Touch Grass".

Plan Pocket card Phone down Printed card
Form Card Phone down Print

Screenshots are from one…

MIT licensed. Two scripts download llama.cpp and the model into the project folder (no sudo, no GPU, no API keys).
Tests run offline against recorded data.

How I Built It

About 1,500 lines of standard-library Python plus a one-page HTML and JavaScript front end (no build step, no
CDN), built on four open pieces:

  1. Fetch. One Overpass query gets every named park, garden, water body, viewpoint and footpath within walking range from OpenStreetMap (ODbL). A second, optional query gets what makes a place specific: unnamed ponds, individually mapped trees, benches, taps, toilets, playgrounds. Open-Meteo (CC BY 4.0) gives the forecast, sunrise, sunset and air quality for exactly my time window. Everything is cached, with a proper User-Agent and slow retries, because the public Overpass servers are shared (I hit plenty of 504s while building this).
  2. Compute. Code works out walking time (4.5 km/h, times 1.3 because paths are never straight), time there, turn-back time, the sunset cut-off, the light ("early morning", "before sunset"), and the bearing and straight-line distance to the nearest edge of each place. It also works out what is mapped inside each place's outline. It skips cemeteries, drains, private land and unnamed spots, and ranks the rest by kind, size and distance.
  3. Ask Gemma. Gemma 4 E2B-it (Apache 2.0, Q4_0 GGUF, 2.84 GB) runs in llama.cpp's llama-server (MIT). It gets the five best candidates as JSON, each with its named ponds, what else is mapped there and "notice" ideas built from those features and the light. It returns one place_id, a headline, a "why", three things to notice and a phone-down line. The place_id is an enum in a JSON schema, so it cannot pick a place I didn't fetch.
  4. Check. A validator rejects the draft if it names a place that isn't in the map data, talks up a different place, uses a number or clock time that isn't in the facts, mentions water or a view the map doesn't support, uses stock filler ("offers a pleasant spot"), or looks cut off. One retry, then a plain template. Every card says who wrote it.

Two bugs that taught me how Gemma 4 behaves on a CPU

  • Thinking skipped the grammar. Through the chat endpoint, Gemma 4 sometimes "thought" for 400 tokens before writing any JSON, and the grammar never kicked in: about 47 seconds, no answer. Rendering the chat template myself (llama-server's /apply-template) and sending it to /completion put the grammar on the very first token. Since then, every answer in my benchmark has been valid JSON.
  • Length limits cut sentences. A maxLength in the schema doesn't make the model write shorter; it stops it mid-word. One early line ended "…focus on the" plus a stray Korean character. I loosened the limits, asked for "one sentence under 15 words", and added checks for cut-off text and non-English characters. The turn-back time is now added by code, so the model can't get it wrong.

Making a 2B model specific

My first cards were true but bland: "Enjoy the park's shade and greenery" (from my first benchmark, docs/bench-results.json). The model only knew a place's name and
kind, so that was all it could say. The fix was better facts, not a longer lecture. Each candidate now arrives
with its named ponds, what OpenStreetMap maps inside it (trees, benches, a tap; without counts, so it doesn't
quote survey numbers), and notice ideas that depend on those features and on the light. Now the same model writes
"Look at the water surface for one full minute at the Lotus Pond" for Cubbon Park at 7 a.m. The checker learned
too: "pond" is allowed only when the map has water there, and a short list of stock phrases is rejected.

Measured on my laptop

Intel Core i5-1035G1 (4 cores, 8 threads, 2019), no GPU, 10 GB RAM, WSL2. Ten plans from scripts/bench.py
(2 cities, 3 start points, 30–90 minutes, morning to evening), on cached map data, with the current prompt:

Measure Result
Plans written by Gemma (not the fallback template) 10 of 10
Drafts rejected by the checker 0
Time per plan, median (min–max) 28.7 s (24.0–32.3 s)
Prompt, median ~1,250 tokens (~860 new, the rest reused from cache), processed at 55 tokens/s
Output, median 134 tokens at 9.7 tokens/s
Cards that mention something mapped at the chosen place 7 of 10 (a simple word match)
llama-bench prompt / generation, 4 threads 61.9 / 9.4 tokens/s
llama-server memory after a plan about 4.3 GB

The richer prompt costs about 4 seconds per plan compared with my first version (median 24.8 s). The laptop was
doing other work too, so times vary. Using the 4 physical cores was a little faster in my runs than all 8 threads
(generation 9.4 vs 7.5 tokens/s, but noisy), so the start script uses physical cores.

Why Does Open Innovation Matter?

Open weights let me control the model, not just prompt it. With a closed API I could only ask the model not
to invent places. With Gemma in llama.cpp I made choosing an unfetched place impossible with a grammar, switched
off thinking to make it fast enough on a CPU, and saw exact token counts and speeds for every call. When thinking
bypassed the grammar, I could find out why, because every layer is open: the model card, the chat template and the
server source.

It's free to run, so it has no reason to keep you. Places and weather come from open data with no keys, and
the model runs on my laptop. No account, no subscription, no per-plan bill. Many apps are paid for with your
attention; this one is happiest when you close it.

No AI company sees where you are. "Where are you, and when are you free?" is personal. The model that reads it
runs on my machine. The only network calls are the open-data lookups (coordinates rounded to about 100 m for
OpenStreetMap and 1 km for Open-Meteo), and they're cached, so a recently fetched area can be planned with
--offline and no connection at all.

Open map data knows the small places. OpenStreetMap has the Bamboo Island Pond inside a Bengaluru park and the
Sopan Baug garden in Pune because volunteers mapped them. Commercial places APIs tend to put businesses first.
OpenStreetMap is a map made by people who walk.

It's also swappable: any chat model llama.cpp can run can replace Gemma by pointing MODEL_FILE at another GGUF file.

Limitations

  • Walking times are estimates (straight line Ă— 1.3), not real routing, and the arrow is a straight line too. Big parks are measured to the edge of their bounding box.
  • It only knows what OpenStreetMap knows. Unnamed places are skipped, and opening hours aren't enforced. "Mapped here" uses each place's bounding box, so it can include things just beside it.
  • Gemma 4 E2B (about 2B effective parameters) is now specific but sometimes stiff ("The water surface is present"). The checker stops invented facts; it doesn't make the writing brilliant.
  • About 29 s on my CPU once the area's map data is cached. The first plan in a new area also waits for OpenStreetMap (up to about 15 s in my runs). That wait is screen time; plan the evening before, or look away.
  • The 60-second budget measures this page, not your phone.

How AI helped

An AI coding assistant (Claude) helped me plan and build this. It drafted most of the code, the tests and a first
version of this post. I reviewed the design rules (code computes every fact, the model only chooses and phrases,
and every card says who wrote it), ran the app on my laptop, checked the numbers against the benchmark files and
edited the text. The product itself uses no closed or paid AI: only Gemma, running locally.

Prize Categories

  • Best Use of Gemma: Gemma 4 E2B runs on a 2019 laptop CPU at under 30 s a plan, and because the weights and chat template are open, I could render the template myself, switch thinking off and put a JSON grammar on the first token. Result: in two 10-plan benchmarks, all 20 plans were written by Gemma (no template fallback), and the model can't name a place I didn't fetch.

Map data © OpenStreetMap contributors (ODbL). Weather and air quality: Open-Meteo.com (CC BY 4.0).

Top comments (0)