{"slug": "i-built-an-ai-app-that-wants-you-to-close-it", "title": "I Built an AI App That Wants You to Close It", "summary": "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.", "body_md": "*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass](https://dev.to/challenges/hacktoberfest-week1-2026-10-05)*\n\nMost 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.**\n\nOFFLINE 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:\n\n*You're done. Close OFFLINE.*\n\nOFFLINE is an AI adventure guide **designed to become unnecessary**.\n\n**The flow takes about thirty seconds of screen time:**\n\n**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.\n\n**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.\n\n*An AI adventure guide designed to become unnecessary.*\n\nCreated by **Noorin Sakhi**.\n\nBuilt for **Hacktoberfest 2026, Challenge 2: Open-Source AI (\"Touch Grass\")**.\n\nYou 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.\"**\n\nIt is deliberately *not* a chatbot, a hiking planner, or a map app.\n\nMost 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…\n\n**Run it in about five minutes** (you need Python 3.10+, Node 20.19+ or 22.12+, and [Ollama](https://ollama.com)):\n\n```\n# 1. a Gemma model (any Gemma tag works)\nollama pull gemma3:1b            # small and fast; or a larger one if your machine can take it\n\n# 2. backend\ncd backend\npython3 -m venv .venv && source .venv/bin/activate\npip install -r requirements.txt\nOFFLINE_MODEL=gemma3:1b python3 -m uvicorn app.main:app --reload\n\n# 3. frontend (new terminal)\ncd frontend\nnpm install && npm run dev       # open http://localhost:5173\n```\n\nNo 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.\n\n``` php\nReact + TypeScript + Vite  ->  FastAPI  ->  Ollama  ->  Gemma (open weights)\n                                  |\n              validate -> safety screen -> retry once -> hand-written fallback\n```\n\n**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.\n\nMy first missions were safe and samey. \"Take a deep breath and notice your surroundings\" is not a quest. Three changes fixed most of it:\n\n```\nLENSES = [\n    \"sound: what you hear, where it comes from, what is just out of earshot\",\n    \"age: things that were here long before you, and things that will outlast today\",\n    \"edges: borders, corners, thresholds, where one place turns into another\",\n    # ...nine more\n]\n```\n\nA 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:\n\n| Layer | What it does | \n|---|---|\n| **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. | \n| **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. | \n| **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. | \n\nIf 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.\n\nI 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:\n\n*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).\n\nI redesigned the interface three times, and it's the part I learned the most from:\n\nChoices 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.\n\nIn 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.\n\nI 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:\n\n`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.\nThere'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.\n\nOFFLINE is deliberately small, and I'd like to keep it that way. If I keep building, it would be:\n\n**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.\n\nMade by **Noorin Sakhi**, MIT licensed. If you try it, I'd love to hear what your mission was and what you noticed.", "url": "https://wpnews.pro/news/i-built-an-ai-app-that-wants-you-to-close-it", "canonical_source": "https://dev.to/ns007-dev/i-built-an-ai-app-that-wants-you-to-close-it-3kff", "published_at": "2026-10-09 03:57:01+00:00", "updated_at": "2026-10-09 04:16:25.546055+00:00", "lang": "en", "topics": ["ai-products", "large-language-models", "generative-ai", "ai-tools", "ai-safety"], "entities": ["Noorin Sakhi", "OFFLINE", "Gemma", "Ollama", "Hacktoberfest", "FastAPI", "React", "Vite"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/i-built-an-ai-app-that-wants-you-to-close-it", "markdown": "https://wpnews.pro/news/i-built-an-ai-app-that-wants-you-to-close-it.md", "text": "https://wpnews.pro/news/i-built-an-ai-app-that-wants-you-to-close-it.txt", "jsonld": "https://wpnews.pro/news/i-built-an-ai-app-that-wants-you-to-close-it.jsonld"}}