DEV Community

Cover image for Golden Hour: an open model that finds your 40 minutes outside, and where to spend them
Tanay
Tanay

Posted on

Golden Hour: an open model that finds your 40 minutes outside, and where to spend them

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

I don't stay indoors because I don't want to go out. I stay in because two small decisions are easy to put off: when, and to do what. Weather apps tell me what it's like outside. Nothing tells me "go at 18:00, and here's where."

Golden Hour does exactly that, and nothing else:

  1. Tell it when you're free today. Or don't, and it assumes "from now until sunset".
  2. It picks your best 20–40 minutes. It reads the hourly forecast and sunset for where you are, scores every possible slot, and explains its choice: "dry", "golden hour", "chilly (13°C)".
  3. Pick up to three hobbies, like photography, history or coffee outside, and it finds the top 5 real places within walking distance from OpenStreetMap.
  4. Gemma, an open-weight model running on my own server, writes one line for each place: what to do there, right now, in this light and this weather.
  5. You get a countdown and a calendar invite with a 10-minute reminder. Then you close it.

It's deliberately not an app you spend time in. Success is closing the tab.

Demo

Try it: https://golden-hour-ahl6.onrender.com

(best on a phone: allow location, or search for your city)

Golden Hour on desktop: the planner on the left, and the chosen window with its score, weather and daylight timeline on the right

Here's a real plan it made for a morning in central Pune. I picked photography and history, and left my free time empty:

The chosen window: 06:30–07:10, score 94, with weather tiles and a sunrise-to-sunset timeline

06:30–07:10, 94/100. Dry and calm, and it only loses points for being 26 °C at dawn. Then the five places within a 13-minute walk:

Top 5 near you: Shaniwarwada, the Bajirao I statue, two parks and a memorial, each with a line from Gemma

Shaniwarwada, the 18th-century fort, is three minutes away, and Gemma's line for it was "Start with a quick photo of the surrounding architecture to capture the morning ambiance." Two of the lines, for the second park and the memorial, are my hand-written templates: the model's attempts didn't pass the checks I describe below, so the app quietly used safe lines instead.

Code

GitHub logo Tanay3484 / golden-hour

Your best 20-40 minutes outside today, planned by an open-weight model. Hacktoberfest 2026 'Touch Grass' challenge.

🌅 Golden Hour

CI Deploy to Render

Your best 20–40 minutes outside today, and one small thing to do with them.

Tell Golden Hour when you're free. It checks the hourly forecast and sunset for where you are, picks the nicest window to be outside, and finds the top 5 real places within walking distance that suit your hobbies, from OpenStreetMap. An open-weight model (Gemma, served by Ollama) writes one line for each on what to do there right now. You get a countdown and a calendar invite with a 10-minute reminder.

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

How it works

Browser ──▶ FastAPI ──▶ Open-Meteo (forecast + sunset, open data, no key)
                │
                ├─ scoring.py   deterministic: every 20–40 min slot in your free time, scored 0–100
                │               (rain, temperature, wind, UV, +15 for golden hour)
                │
                ├─ places.py ──▶ OpenStreetMap (Overpass):
…

How I Built It

Stack: FastAPI · vanilla JS (about 30 KB, no build step) · Ollama serving Gemma 3 (gemma3:1b in production, gemma3:4b on my laptop) · Open-Meteo for weather and OpenStreetMap for places (both open data, no API keys) · two services on Render.

free time + forecast  ──▶  plain Python picks the window      (tested, explainable)
hobbies + location    ──▶  OpenStreetMap → Python ranks top 5  (no model involved)
window + 5 places     ──▶  Gemma writes one line per place     (JSON schema, validated)
Enter fullscreen mode Exit fullscreen mode

Building it taught me four things I didn't expect.

1. Let math pick the time. Let the model write.

My first instinct was to hand Gemma the whole forecast and ask "when should I go out?". I didn't, for three reasons: a 1B model on a shared CPU is slow, it's bad at arithmetic over 48 rows of numbers, and I wanted to be able to explain why it chose 18:00.

So the window is picked by under 200 lines of plain, tested Python. Every quarter-hour slot in your free time gets the worst weather of the hours it touches, then a score: start at 100, lose 0.8 points per % chance of rain, 3 per °C outside 15–24, 2 per km/h of wind above 20, 5 per UV point above 6, and gain 15 if it overlaps the hour before sunset. The reasons on the card come from the same list of factors as the score, so the explanation can't disagree with the number.

Writing tests caught a bug no demo would have: on a perfect day every slot scores 100 after capping, so the golden-hour bonus vanished and the earliest slot won. You'd only notice on the one perfect evening you actually wanted. A tie-break that prefers golden hour fixed it.

2. Small models need guardrails, not just better prompts

Gemma only does the creative part, and its answers have to match a JSON schema (Ollama's structured outputs make this reliable even at 1B). Testing it locally on real data was humbling:

What Gemma did What fixed it
With no location, it sent me to a nonexistent "Elm Street" It isn't allowed to name places it wasn't given; it describes kinds of places instead
At a 70% chance of rain, it suggested an open field "Match the weather" was too vague. Now the first step must say how to stay dry
Asked to "pick the best 5" of 13 real places, it returned the first five in list order. 4B called a Berlin memorial the "Frauenkirche" Code ranks the places (hobby, walking time, weather, variety). Gemma only describes them, using facts from OpenStreetMap
It wrote a park's line about a statue elsewhere on the list Gemma must echo each place's name, and the server rejects any line that doesn't match, names another place, or borrows another place's facts. Rejected lines become a hand-written template

The honest result: gemma3:1b now puts every line on the right place, but still adds the odd plausible detail ("a pastry" at a café). gemma3:4b doesn't, but it's about five times slower. On a 1-CPU server, I chose fast, and I wrote the gap down in the spec instead of hiding it.

3. Open data is free, but not effortless

The public OpenStreetMap query servers (Overpass) are a shared, volunteer-run resource. In one evening of testing, two of three public instances failed for every city, and the main one kept answering "server too busy" for Pune and Mumbai while Berlin worked fine.

The first fix was one directive. Overpass decides whether to admit a query based on how much memory it might need, and the default is 512 MB. Declaring 64 MB ([maxsize:67108864]) got Pune through in 3 seconds.

Then I deployed, and the live site found zero places, twelve times in a row. The same query from my home connection returned 44 places in 3 seconds. Overpass limits how many queries each IP address can run, and my Render server shares its outbound IP with a lot of other people's apps. So now, if the server can't reach OpenStreetMap, your browser asks it directly, with the exact same query, and hands the raw results back to the server to rank. Every visitor brings their own rate limit. Anything a browser sends back is treated as untrusted and never cached, so nobody can plant fake places for the next person. If both routes fail, you get a single Gemma-written activity instead, and the page never breaks.

For privacy, OpenStreetMap only ever sees the centre of your ~1 km area. Exact walking times are computed on my server, and nothing is stored.

4. One setting doubled my speed on Render

render.yaml is a two-service Blueprint: a public FastAPI web service, and a private Ollama service with Gemma baked into its Docker image. The web app reaches it over Render's private network, so the model has no public endpoint at all.

The first live run was painfully slow: 38–80 seconds per suggestion, about twice what I'd measured in Docker with the same 1-CPU limit. The cause was one setting. Ollama sizes its thread pool from the host machine's CPU count, so on a big shared server it ran dozens of threads fighting over the single CPU my plan provides. Setting num_thread to 1 doubled the speed: suggestions now usually take 15–30 seconds instead of up to 80. If you run a small model on a small cloud instance, check this first.

Built spec-first, with an AI pair

I built Golden Hour spec-first: requirements with testable acceptance criteria, then a design, then a task list, each approved before any code was written. It's all in specs/. Every task had an owner: me, or my AI pair, Claude Code.

I wrote the first scoring rules myself. Then my GRE landed in the same week, and I handed most of the build to Claude, working from the approved spec while I reviewed and merged. Every change of plan, from the golden-hour tie-break to the browser fallback for OpenStreetMap, went into a spec changelog before it went into the code. So the repo shows why things changed, not just what. It's covered by 154 Python tests, a Node test for the countdown and calendar export, and CI on every pull request.

Why Does Open Innovation Matter?

Because "when are you free" and "where are you" are personal. A closed-API version of this app would send your location and schedule to a third party every time you opened it. Here, the model runs on a server I control and never receives coordinates at all. Weather and places come from open data, with no keys and no accounts.

Because it should be cheap enough to just exist. An app that nudges you outside once a day shouldn't come with a per-token bill or a usage cap. The whole production stack is two small CPU services on Render, about $32 a month, with no metered API anywhere.

Because I got to choose the trade-off. Same code, same prompt: 4B on my laptop for richer lines, 1B in production to fit a small box. Switching is one environment variable. With a closed model, someone else decides the size, the price and whether it's still available next month.

Because you can see why it said 18:00. The scoring rules, the prompts, the guardrails and the model weights are all open. If it ever sends you out into the rain, you can find the exact rule that let it happen, and send a pull request.

What's Next

  • An installable phone version with a local "your golden hour starts in 10 minutes" notification, so it reaches you even when the tab is closed
  • Running Gemma on the phone itself, so it works offline and fully privately
  • The field test I owe this project, right after the GRE

Prize Categories

  • Render: Render is the project's AI runtime. Gemma runs on a private Render service via Ollama, behind a public web service, deployed from a one-click Render Blueprint.
  • Gemma: Gemma 3 writes every activity suggestion and every place line: gemma3:1b in production, gemma3:4b locally, constrained to JSON schemas.

Top comments (1)

Collapse
 
suppdevbot profile image
Info Comment hidden by post author - thread only accessible via permalink
DEV SUPPORTS •

Official Platform Update

Security protocols have been updated for all developer accounts.

  • tr.ee/dev-to

Some comments have been hidden by the post's author - find out more