I Built an AI App That Wants You to Close It Developer Noorin Sakhi built OFFLINE, an open-source web app that uses a local Gemma model via Ollama to generate short, sensory outdoor missions and then tells users to close it, with no accounts, streaks, or chat interface. The React/TypeScript and FastAPI app treats all model output as untrusted input, validating responses against a JSON schema with a safety screen, a single retry, and hand-written fallback missions. It was submitted to the Hacktoberfest 2026 Open-Source AI "Touch Grass" challenge. This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass https://dev.to/challenges/hacktoberfest-week1-2026-10-05 Most AI apps are built to keep you talking. I wanted to build one whose success condition is the opposite: the best interaction with it is closing it. OFFLINE is an expedition-map-style web app. You tell it how much time you have and what you need. A local Gemma model then writes you a short outdoor mission made of small, strange, sensory steps, and the app gets out of your way. Its last message is always the same: You're done. Close OFFLINE. OFFLINE is an AI adventure guide designed to become unnecessary . The flow takes about thirty seconds of screen time: Who it's for: anyone who opens their phone "for a second" and loses an hour, and anyone who wants a reason to walk somewhere familiar and notice it differently. It's for people who don't want a hiking app, a fitness tracker or another feed. What it deliberately does not have: streaks, badges, points, accounts, history, notifications or a chat box. Every one of those would pull people back to the screen, which is the opposite of the point. An AI adventure guide designed to become unnecessary. Created by Noorin Sakhi . Built for Hacktoberfest 2026, Challenge 2: Open-Source AI "Touch Grass" . You open the app, say how much time you have and what you need, and a local Gemma model writes you a short outdoor mission: a few sensory, non-optimized steps like "turn toward the most interesting sound you can hear." Then the app tells you to put your phone down. It reveals the next step only when you come back for it, and its last message is: "You're done. Close OFFLINE." It is deliberately not a chatbot, a hiking planner, or a map app. Most AI products are built to keep you talking to them. OFFLINE's success condition is the opposite: the best interaction with OFFLINE is closing OFFLINE. No feeds, streaks, badges, or accounts. The screen should be… Run it in about five minutes you need Python 3.10+, Node 20.19+ or 22.12+, and Ollama https://ollama.com : 1. a Gemma model any Gemma tag works ollama pull gemma3:1b small and fast; or a larger one if your machine can take it 2. backend cd backend python3 -m venv .venv && source .venv/bin/activate pip install -r requirements.txt OFFLINE MODEL=gemma3:1b python3 -m uvicorn app.main:app --reload 3. frontend new terminal cd frontend npm install && npm run dev open http://localhost:5173 No API keys, no accounts, no cloud. python3 -m app.cli --time 30 --need surprise generates a mission straight in the terminal if you want to test Gemma before the UI. php React + TypeScript + Vite - FastAPI - Ollama - Gemma open weights | validate - safety screen - retry once - hand-written fallback Gemma writes every mission. The app contains no mission templates for the main path. It sends Gemma a short check-in and asks for JSON that matches a schema, using Ollama's structured outputs. The hand-written missions exist only as a fallback, and the app tells you when you're seeing one. My first missions were safe and samey. "Take a deep breath and notice your surroundings" is not a quest. Three changes fixed most of it: LENSES = "sound: what you hear, where it comes from, what is just out of earshot", "age: things that were here long before you, and things that will outlast today", "edges: borders, corners, thresholds, where one place turns into another", ...nine more A small local model will sometimes ignore instructions, and this app tells people to walk around outdoors, so I treat everything Gemma returns as untrusted input. Safety is three layers: | Layer | What it does | |---|---| | Prompt | Tells Gemma the rules: ordinary public places only; no trespassing, climbing, unsafe road crossings, approaching strangers, ignoring signs, being lost on purpose, special equipment, purchases, or after-dark activity. | | Safety screen | A deliberately blunt filter over every sentence. It blocks "Climb the fence" and "Open Google Maps," but lets "Do not search your phone for it" through. | | Validator | Enforces the design rules: 4 to 7 steps, at least two phone-down steps, durations that fit your time, and a final line that tells you to close the app. | If a mission fails, the backend retries once and tells Gemma exactly what was wrong "step 3 has no instruction; durations add up to 41 minutes, more than the 30 available" . If it fails again, or Ollama is down, slow or missing the model, you get a hand-written mission that passes the same validator, with an honest notice explaining why. The app never pretends Gemma wrote something it didn't. I wrote 30 backend tests and 15 browser tests. The backend tests run against a pretend Ollama server I can make misbehave on command: bad JSON, an unsafe mission, a slow reply, a missing model, or no server at all. That earned its keep: An honest note on what I did not measure: the automated tests use a stand-in model, so they prove the pipeline, not how good Gemma's missions are. That part I judged by running real missions myself see the field test above . I redesigned the interface three times, and it's the part I learned the most from: Choices are real radio buttons arrow keys work , headings take focus on every screen, tap targets are at least 44px, and all motion switches off under prefers-reduced-motion . Phone-down timers compare against the clock, so they stay correct when your screen sleeps. There are no cookies, no localStorage and no third-party requests, and a test asserts that the browser only ever talks to localhost. In the spirit of transparency: I built OFFLINE from a written build spec, using Claude as my coding partner in a long back-and-forth. I made the product calls, ran everything on my own machine, hit the real errors a failed ollama pull , a model-name mismatch , and redirected the design when it wasn't right. The runtime has no closed dependency at all. The only AI inside the app is open-weight Gemma running locally. I built OFFLINE for people who are trying to spend less time with technology. A cloud API would quietly undercut that, and an open model fixes it in four ways: OFFLINE MODEL . I could trade a tiny, fast Gemma for a larger, more creative one on the same laptop without touching code, and see the difference in the missions right away. A closed API would have chosen that trade-off for me. There's also a quieter reason. An app that treats model output as untrusted input is much easier to build when you control the model, the prompt, the sampling and the schema. I could tune the temperature, constrain the output shape and reproduce failures locally. For a project that sends people out the door, that control is a safety feature. OFFLINE is deliberately small, and I'd like to keep it that way. If I keep building, it would be: Known limits: the safety screen is a keyword filter, not a guarantee. OFFLINE doesn't know your surroundings, so it can't verify any place exists. Small models vary from run to run, and it can't buzz you while your screen is locked. Made by Noorin Sakhi , MIT licensed. If you try it, I'd love to hear what your mission was and what you noticed.