DEV Community

Yash Dharme
Yash Dharme

Posted on Edited on

Side Quest: an AI that sends you on a walk, and never learns where you are

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

What I Built

Side Quest turns the streets around you into a short scavenger hunt.

You tell it how long you have (15 to 60 minutes) and what kind of clues you like (playful, poetic, detective, or for kids). It finds a few interesting places within walking distance (a temple, a mural, an old bookshop, a pond) and plans a loop that fits your time. Then Gemma, running inside your browser, writes a riddle for each stop.

Then you put the phone away.

When you think you've found the place, you check in. The app shows you one small thing to notice before you move on: count the bells, find the oldest crack in the wall, listen for one sound that repeats. At the end, Gemma writes a three-line postcard about the walk. It is saved only on your device.

Most "AI + outdoors" apps keep you looking at the screen while you're outside. I wanted the opposite: the screen is on for about ten seconds per stop, and the rest of the time you're looking at the street.

Demo

You need a browser with WebGPU for the model: Chrome on Android 12+, or Chrome/Edge on Windows or macOS. The first run downloads Gemma once (~0.7 GB). Without WebGPU, the walk still works with simple built-in clues.

Code

Side Quest

A short walking scavenger hunt around wherever you're standing. The clues are written by Gemma, running entirely in your browser (WebGPU via WebLLM) Your location never leaves the phone.

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

How it works

  1. Pick how long you have (15–60 min) and a clue style (playful, poetic, detective, for kids).
  2. The app finds interesting places nearby from OpenStreetMap and Wikipedia: parks, temples, murals, old trees, bookshops, historic buildings.
  3. A small planner picks a loop of 2–4 varied stops that fits your time.
  4. Gemma writes a riddle for each stop plus one small thing to notice when you get there.
  5. Put the phone away. Check in when you think you've found it (it buzzes if the screen is on).
  6. At the end, Gemma writes you a three-line postcard of the walk, saved only on your device.

Why

…

MIT licensed. About 1,000 lines of TypeScript, no framework.

How It Works

your phone ──► snap location to a ~1 km grid square
           ──► OpenStreetMap + Wikipedia: "what's interesting in this square?"
           ──► planner: pick 2–4 varied stops that fit your time
           ──► Gemma (WebGPU, in a Web Worker): write a riddle per stop
           ──► walk · check in · notice something · next clue
           ──► Gemma: write a postcard of the walk → saved on the device
Enter fullscreen mode Exit fullscreen mode

1. Finding places, with two free and keyless sources. OpenStreetMap (via the public Overpass API) gives the small stuff: parks, temples, murals, named bakeries, old trees. Wikipedia's geosearch API gives named landmarks with a line of description, which turns out to be great riddle material. Near Shaniwar Wada in Pune, one query returned 188 candidate places.

2. Planning the loop, deliberately without AI. A small randomised search tries 400 greedy loops and keeps the one with the most stops, the most variety (it heavily penalises a second temple in a row), and a total time closest to what you asked for. Walking speed is 70 m/min and streets get a 1.3× detour factor. A planner is the right tool here: it's deterministic and checkable, and a 1B model is bad at geometry.

3. Writing the clues, which is where the model earns its place. Each stop's OpenStreetMap tags and Wikipedia summary go to Gemma with one job: hint at the place without naming it, and suggest one thing to notice with your senses.

const prompt = `You write clues for a real-world walking scavenger hunt.

The walker must find this place: ${describe(place)}.

Write:
1. A riddle of at most two short sentences that hints at the place without saying its name.
   Use what someone would see, hear or smell there.
2. One small thing to notice once they arrive, using their senses. One sentence.

Tone: ${VOICE[vibe]}. No emoji. No hashtags.

Reply in exactly this format and nothing else:
CLUE: <riddle>
NOTICE: <thing to notice>`;
Enter fullscreen mode Exit fullscreen mode

If the model's answer can't be parsed, or it gives the name away, a template clue takes over and the screen says so honestly.

Why Open Matters Here

This is the part I care about most.

A hosted model would need your live location for every clue. Here nothing about where you're standing ever reaches an AI provider, because the AI provider is your own GPU. Gemma runs in the browser through WebLLM on WebGPU, inside a Web Worker.

The only network requests are the map lookups, and even those never carry your exact position. The query box is snapped outward to a 0.01° grid (about 1.1 km) before it leaves the phone:

/**
 * A bounding box snapped outward to a 0.01° grid (~1.1 km). This is the only
 * thing about your location that ever leaves the phone: the map server learns
 * which grid squares you asked about, never the point you're standing on.
 */
export function snappedBbox(c: LatLng, radiusM: number) { … }
Enter fullscreen mode Exit fullscreen mode

Everyone standing anywhere in the same square sends the identical request.

The rest follows from using open pieces:

  • It costs nothing to run. There are no API keys and no accounts. It's a static site plus one tiny cached relay function on Vercel's free tier. With a closed model API I'd be paying per riddle, or asking you for a key.
  • It works offline after the first run. The model weights are cached by the browser, and map results are cached per area for a week, so a second walk in the same neighbourhood works with no signal at all.
  • The model is swappable. You can pick Gemma 3 1B (~0.7 GB, sized for phones) or Gemma 2 2B (~1.4 GB, better clues on a laptop). Swapping is one line in MODELS.

What Went Wrong (and What I Changed)

  • The public Overpass server turns away browsers. It worked from my dev machine, then failed on the deployed site: overpass-api.de answers anonymous browser requests with 406 Not Acceptable and no CORS header, so the browser reports a CORS error that hides the real reason. The server wants clients to identify themselves, and a browser isn't allowed to set User-Agent. The fix is a ~50-line Vercel function, api/places.ts. It accepts only a grid-snapped box (anything off-grid or over ~9 km is rejected), sends the fixed query with an identifying User-Agent, falls back across four mirrors, and sets s-maxage=604800 so the CDN answers repeat boxes for a week without touching OpenStreetMap at all.
  • Even when it identified itself, Overpass was often overloaded, with 504 Gateway Timeout more often than not during testing. So OpenStreetMap and Wikipedia run in parallel, the relay starts its mirrors a few seconds apart and takes the first complete answer, and either source alone is enough to plan a walk.
  • Overpass sometimes "succeeds" with nothing. Under load it can give up mid-query and still answer 200 OK with an empty list and a remark saying it timed out. The app read that as "nothing here". Those answers are now treated as failures, and an empty area is only cached for an hour.
  • Wikipedia was silently dropping most results. The geosearch generator returned 47 pages, but only 10 had coordinates attached. prop=coordinates has its own limit (colimit) that defaults to 10. Adding colimit=max took the area from 1 usable landmark to dozens.
  • 'gpu' in navigator is not enough. A browser can expose WebGPU and still have no usable adapter. Chrome on Linux does exactly that by default. The check now actually calls requestAdapter(), and if the model can't start, the walk still happens with built-in clues instead of an error screen.
  • Laptops don't really know where they are. Desktop browsers locate by Wi-Fi or IP and can be kilometres out. When a lookup comes back empty, the app now says how coarse your location is and offers a demo walk instead of a dead end.
  • Browsers pause GPS when the screen is off. I can only buzz you on arrival while the screen is on. Otherwise you tap I think I'm here. Honestly, that turned out to be part of the fun.

What's Next

  • Fine-tune a small Gemma on good riddles, since the 1B model's clues vary in quality.
  • A "share this quest" link, so friends can walk the same route without either of you sending your location anywhere.
  • Hindi and Marathi clues.

Prize Categories

Best Use of Gemma. Gemma is the only AI in the app. It runs entirely on-device via WebGPU and writes every clue and postcard.


Map data © OpenStreetMap contributors (ODbL). Wikipedia text CC BY-SA. Built with AI assistance (Claude Code) for the scaffolding and debugging.

Top comments (1)

Collapse
 
ai_adam profile image
ai_adam •

Concept "never learns where you are" rất thú vị — thay vì thu thập dữ liệu vị trí để "cá nhân hóa", nó dùng randomness hoặc heuristic cục bộ (thời tiết, giờ, độ an toàn của khu vực chung) để gợi ý lộ trình. Cách này tránh hẳn việc phải xin permission location, giảm surface area cho tracking, và vẫn đạt mục đích "touch grass".

Một vài góc kỹ thuật có thể đào sâu thêm:

  • Entropy source: dùng seed từ timestamp + hash của IP coarse (chỉ ra city-level) để deterministic trong session nhưng không linkable cross-session
  • POI selection: pré-load OpenStreetMap tiles offline cho bounding box lớn, filter client-side theo tags (park, waterway, pedestrian) — zero round-trip server
  • Safety scoring: aggregate crime/lighting data ở granularity ward/commune, không cần GPS chính xác
  • Battery: geofence nhẹ (network-based) chỉ để trigger "bạn đã ra khỏi nhà", không log trail

Hacktoberfest week 1 focus "touch grass" hợp lý — nhiều dev ngồi debug 10h/ngày quên rằng environment context (ánh sáng, không khí, chuyển động) ảnh hưởng trực tiếp đến cognitive load. Tool này nếu open-source tốt có thể thành reference implementation cho privacy-first wellness apps.

Curious: bạn handle edge case "user ở vùng vắng ít POI" như thế nào? Fallback về (site: labagent .tech)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.