<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ayushya Ranjan</title>
    <description>The latest articles on DEV Community by Ayushya Ranjan (@dagger7164).</description>
    <link>https://dev.to/dagger7164</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4156357%2Fa0ca8ffb-27db-4cb5-8139-6b42db4b7649.jpg</url>
      <title>DEV Community: Ayushya Ranjan</title>
      <link>https://dev.to/dagger7164</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dagger7164"/>
    <language>en</language>
    <item>
      <title>I Made a Game That Only Counts If You Leave the House</title>
      <dc:creator>Ayushya Ranjan</dc:creator>
      <pubDate>Sat, 10 Oct 2026 22:42:39 +0000</pubDate>
      <link>https://dev.to/dagger7164/i-made-a-game-that-only-counts-if-you-leave-the-house-6b0</link>
      <guid>https://dev.to/dagger7164/i-made-a-game-that-only-counts-if-you-leave-the-house-6b0</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I spend most of my day on a screen. So I built &lt;strong&gt;Touch Grass&lt;/strong&gt;, an app that gives me a reason to leave it.&lt;/p&gt;

&lt;p&gt;Every morning, you get one outdoor task. "Find moving water." "Say hello to three people you pass." "Pick up ten pieces of litter." You can do it any time that day. Then you take a live photo as proof. A small AI on my laptop looks at the photo, and plain code decides if you passed.&lt;/p&gt;

&lt;p&gt;The tasks are not only about nature. Some are about people: ask a shop owner how long they've been open, or ask someone for their favorite place to eat. The goal is to go out, look around, and talk to someone, not just count steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb1qtns7imeqxyg03lt00.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb1qtns7imeqxyg03lt00.jpeg" alt=" " width="720" height="1600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Today's task. Anytime today. No timer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;40 hand-written tasks in six kinds: nature, movement, exploration, social, creative and community&lt;/li&gt;
&lt;li&gt;Live camera only. No gallery, so the proof comes from really being there&lt;/li&gt;
&lt;li&gt;If the AI gets it wrong, tap "Mark done anyway" for half points. The app never blocks your streak&lt;/li&gt;
&lt;li&gt;Works with no signal. The photo waits on your phone and goes through when you're back online&lt;/li&gt;
&lt;li&gt;XP, levels, a daily streak and a monthly count&lt;/li&gt;
&lt;li&gt;You choose which kinds of tasks you want more or less of, and how bold the social ones are&lt;/li&gt;
&lt;li&gt;Weather-aware tasks, a streak freeze, a daily reminder, a bad-day mode and a monthly recap&lt;/li&gt;
&lt;li&gt;It counts its own screen time, because the screen should be the shortest part of the day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's for people like me who want a small push to go outside, and a little game to keep it going.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64uqt88uecplq18ei12f.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64uqt88uecplq18ei12f.jpeg" alt=" " width="720" height="1600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;No signal? The photo is saved on the phone and waits.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffsdxe3cqfkljrvkgl3ce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffsdxe3cqfkljrvkgl3ce.png" alt=" " width="800" height="1779"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Back online. The laptop checked the photo and the task is done.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/V403W-E0q_o" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AyushyaRanjan" rel="noopener noreferrer"&gt;
        AyushyaRanjan
      &lt;/a&gt; / &lt;a href="https://github.com/AyushyaRanjan/TouchGrass" rel="noopener noreferrer"&gt;
        TouchGrass
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Touch Grass&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/AyushyaRanjan/TouchGrass/docs/logo.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FAyushyaRanjan%2FTouchGrass%2FHEAD%2Fdocs%2Flogo.jpg" alt="Touch Grass logo" width="128"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Single-user app that gives you one daily outdoor task (nature, movement, exploration, social, creative, community). You submit a live photo as proof from a mobile app; a laptop server verifies it with a local VLM served by Ollama.&lt;/p&gt;

&lt;p&gt;See &lt;code&gt;AGENTS.md&lt;/code&gt; for the full spec, hard rules and game/verdict rules.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Prerequisites&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.astral.sh/uv/" rel="nofollow noopener noreferrer"&gt;uv&lt;/a&gt; (Python package manager)&lt;/li&gt;
&lt;li&gt;Python 3.11+&lt;/li&gt;
&lt;li&gt;Node.js 20+ (for the Expo app in &lt;code&gt;app/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ollama.com" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt; running locally with a vision model
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;ollama pull qwen2.5vl:3b&lt;/pre&gt;

&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;Optional: &lt;a href="https://tailscale.com" rel="nofollow noopener noreferrer"&gt;Tailscale&lt;/a&gt; so the phone can reach the laptop over HTTPS-grade WireGuard while the app still uses HTTP&lt;/li&gt;
&lt;li&gt;Optional: an &lt;a href="https://expo.dev" rel="nofollow noopener noreferrer"&gt;Expo&lt;/a&gt; account if you want a standalone APK (&lt;code&gt;eas build&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 1. Install dependencies (creates .venv and uv.lock)&lt;/span&gt;
uv sync
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 2. Configure environment&lt;/span&gt;
copy .env.example .env   &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Windows; use `cp .env.example .env` on macOS/Linux&lt;/span&gt;
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Edit .env and set API_TOKEN to a long random string. Never commit .env.&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AyushyaRanjan/TouchGrass" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The AI in the app is open and runs on my own laptop. It's an everyday machine: an RTX 3050 with 4 GB of video memory and 16 GB of RAM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The AI.&lt;/strong&gt; I used &lt;strong&gt;Qwen2.5-VL (3B)&lt;/strong&gt; through &lt;strong&gt;Ollama&lt;/strong&gt;. It looks at the photo and answers small yes/no questions, like "Is a leaf visible?" It also writes a short note about what it sees, and that note becomes the hint you read in the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rule that made it work: the AI never decides pass or fail.&lt;/strong&gt; Small AI models like to say yes. So plain code makes the call. You pass only if every required question gets a "yes". That also means nobody can sweet-talk the AI into a pass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choosing the model.&lt;/strong&gt; I ran a quick test on 7 photos:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Right answers&lt;/th&gt;
&lt;th&gt;"Not sure" answers&lt;/th&gt;
&lt;th&gt;Speed per photo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2.5-VL 3B&lt;/td&gt;
&lt;td&gt;12 of 12&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3 to 6 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 3 4B&lt;/td&gt;
&lt;td&gt;8 of 10&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4.5 to 6.5 seconds, but 71 seconds on the first photo, and it crashed once&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Gemma also gave "no" answers while its own note said the thing was visible. That's a tiny test, not a benchmark, but it was enough to pick Qwen. Swapping the model took one line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rest of the build:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Photo checks come first.&lt;/strong&gt; Blurry, dark, tiny or repeated photos are stopped before the AI sees them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server:&lt;/strong&gt; Python (FastAPI) and SQLite, running on my laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App:&lt;/strong&gt; Expo (React Native), built as an Android app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The link between phone and laptop:&lt;/strong&gt; Tailscale, a private network between my own devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline:&lt;/strong&gt; the phone keeps a queue. It sends photos when it can reach the laptop, and it never sends the same photo twice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tasks are written by hand,&lt;/strong&gt; because a small AI writes vague tasks. The AI only rewrites the wording a little.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaks and XP are worked out from my history&lt;/strong&gt; each time, so they can't get out of sync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The demo voice-over&lt;/strong&gt; was made with ElevenLabs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Being honest about how I built it:&lt;/strong&gt; I used coding agents (OpenCode and Cursor) to write most of the code, from detailed prompts and a rules file. Those run on hosted models. The AI that runs inside the app is the local one. I built the Android file with Expo's cloud build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What doesn't work well yet:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Social tasks are checked by the place only.&lt;/strong&gt; The AI can see the cafe, but not the chat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The AI is small,&lt;/strong&gt; so it's sometimes unsure or wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My laptop has to be on&lt;/strong&gt; for photos to get checked.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Android only.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faq3ltzdqdhbwby68cedc.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faq3ltzdqdhbwby68cedc.jpeg" alt=" " width="720" height="1600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The app counts its own screen time. That 7 minutes was a testing day.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F97meqw93u68mv3gcg1fy.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F97meqw93u68mv3gcg1fy.jpeg" alt=" " width="720" height="1600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;You decide which kinds of tasks you want more or less of.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Bugs I hit along the way
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A tiny photo was stopped in 0.6 seconds.&lt;/strong&gt; My test photo was only 30 KB. The photo check caught it before the AI even looked, and that's the point of checking first. The message said "move a little closer", which was wrong, because the problem was resolution, not distance. I changed it to say the resolution is too low.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gemma disagreed with itself.&lt;/strong&gt; One of the two models I tried said "no" to "is this outdoors?" while its own note said "sky is visible". It also took 71 seconds on the first photo and crashed once. Qwen got every question it answered right and was much faster. That's why I picked Qwen. It was a small test, but the difference was clear.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I typed the model name wrong, twice.&lt;/strong&gt; My first try at a third model failed with a "not found" error. The tag had a typo. I fixed it and moved on, because Qwen was already doing the job.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A photo of a screen passed the "outdoors" question.&lt;/strong&gt; I photographed a monitor showing a tree, and the AI said "outdoors". When I asked directly "is this a screen?", it got it right. So the model can tell, I just wasn't asking. For now, spotting screens is only logged. It's a clear next step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The same note appeared for every question.&lt;/strong&gt; The small model describes the photo once and pastes that note under each answer. So some hints are less useful than I'd like. A fix would be to ask one question at a time, which costs a few more seconds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One extra retry made a check take 13 seconds instead of 5.&lt;/strong&gt; My rule that every note must be at least four words sometimes made the model try again. It's a fair trade for better hints, but I watch it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Uploads failed with "Unsupported FormDataPart".&lt;/strong&gt; Expo's network code didn't accept the usual way of sending a photo. The server never saw a request. The app also said "couldn't reach your server" for every kind of error, which was misleading. I switched the upload to a different method and made the error messages say what really went wrong.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;My history thumbnails were blank.&lt;/strong&gt; The image requests didn't carry my token, so my own server said "401 unauthorized" to my own app. I now download each thumbnail with the token and keep a copy on the phone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The app showed "done" when the server said it wasn't.&lt;/strong&gt; When I deleted a test photo on the laptop, the phone still remembered it. Today's task showed as finished with no way to upload. I made the phone check with the server and drop items the server doesn't know about, and I added a button to clear the local cache.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Close button did nothing after an "unsure" result.&lt;/strong&gt; I tapped it three times. It only went away when I switched tabs. The cause was the screen being redrawn from fresh data, which brought the card back. Closing now takes effect immediately and stays closed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Expo Go can't open the app in airplane mode.&lt;/strong&gt; It loads the app from my laptop, so with no connection it can't start. The offline queue works once the app is open. For real use I needed a standalone app that doesn't depend on my laptop.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;My photos stay mine.&lt;/strong&gt; They go from my phone to my laptop and nowhere else. No cloud service ever sees them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing per photo.&lt;/strong&gt; I can check as many photos as I want, and nobody sends me a bill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could swap models in one line.&lt;/strong&gt; I tried two, looked at the results, and picked the better one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I set the rules myself:&lt;/strong&gt; the questions, the exact format the model must answer in, and the code that decides. That's why the AI can't just agree with everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The small model made the design better.&lt;/strong&gt; A bigger closed model might see more detail. But because mine is small, I had to break the job into simple yes/no questions and let code decide. That made the app fairer and easier to trust.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An app that tells you to go outside shouldn't send your photos to a company on the way. This one doesn't.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>I Built a Private "Open When" App for a Friend Who Feels Low at Night</title>
      <dc:creator>Ayushya Ranjan</dc:creator>
      <pubDate>Sat, 03 Oct 2026 16:18:01 +0000</pubDate>
      <link>https://dev.to/dagger7164/i-built-a-private-open-when-app-for-a-friend-who-feels-low-at-night-21n8</link>
      <guid>https://dev.to/dagger7164/i-built-a-private-open-when-app-for-a-friend-who-feels-low-at-night-21n8</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;OpenWhen&lt;/strong&gt; is a private comfort app I built for "a close friend", who "often feels low and alone, especially at night".&lt;/p&gt;

&lt;p&gt;When you feel low, generic motivational quotes don't help. What helps is hearing from people who actually know you. So the app works like digital "open when..." letters: people who care about my friend (including me) recorded voice notes and wrote messages, each tagged with a feeling. My friend taps how they feel, optionally types a few words, and the app brings back the right message from a real person. A small local AI model writes a one-line, gentle introduction, but it never tries to be the comforter. The comfort comes from people.&lt;/p&gt;

&lt;p&gt;It also has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log a win:&lt;/strong&gt; a place for my friend to save things they got through, which the app surfaces on harder days ("Remember this?").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I have a challenge:&lt;/strong&gt; the AI breaks a problem into one tiny five-minute step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saved for later:&lt;/strong&gt; favorite messages that stay available even when the server is offline. &lt;em&gt;(remove if not built)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A safety layer:&lt;/strong&gt; if my friend writes something that suggests self-harm, the AI is bypassed completely and the app shows a fixed message with one-tap buttons for Tele-MANAS (14416), my phone number, and 112.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is a comfort tool, not therapy, and the app says so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;


&lt;div&gt;
    &lt;iframe src="https://www.youtube.com/embed/0O-eB5yYAJA" width="315" height="560"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;


&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AyushyaRanjan" rel="noopener noreferrer"&gt;
        AyushyaRanjan
      &lt;/a&gt; / &lt;a href="https://github.com/AyushyaRanjan/OpenWhen" rel="noopener noreferrer"&gt;
        OpenWhen
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Open When...&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Open When... is a small, private web app that keeps a few kind words close by
for someone who is having a hard day. When they say how they feel, it shows them
a message or voice note from someone who loves them, or one of their own logged
wins. It runs entirely on one laptop and talks to no company's servers.&lt;/p&gt;
&lt;p&gt;It is built to be read and changed by one person, not scaled. If you are looking
for a product, this is not it. If you are looking for something warm, small and
yours, it might be.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it is, and who it is for&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The person who uses it is &lt;strong&gt;one specific friend&lt;/strong&gt; who often feels low or alone
They open it on their phone or laptop, tap how they feel, and get back something
human that already existed — a note from a parent,…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AyushyaRanjan/OpenWhen" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Everything that touches my friend's feelings runs on my own laptop (i5-12450HX, 16 GB RAM, RTX 3050 with 4 GB VRAM).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model runtime:&lt;/strong&gt; &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; for local inference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat model:&lt;/strong&gt; &lt;code&gt;[MODEL YOU CHOSE, e.g. qwen2.5:3b]&lt;/code&gt;, a small open-weight model. I compared [LIST THE MODELS YOU TESTED] on sample inputs and picked the warmest one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embeddings:&lt;/strong&gt; &lt;code&gt;[EMBEDDING MODEL, e.g. nomic-embed-text]&lt;/code&gt; for matching a feeling to the right message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; FastAPI and SQLite, with plain NumPy cosine similarity for retrieval. No heavy vector database was needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; a plain HTML/CSS/JS installable PWA with no frameworks, no CDNs and no external fonts, so it works with zero internet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phone access:&lt;/strong&gt; [HOW YOU DID IT: e.g. Tailscale / Cloudflare Tunnel], with a passcode login &lt;em&gt;(remove if not added)&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built with an open-source agent:&lt;/strong&gt; I wrote the build as a sequence of prompts and used &lt;a href="https://opencode.ai" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt; as the coding agent. [MENTION WHICH MODEL POWERED OPENCODE, and note that it only wrote code and never saw my friend's data.]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Design decisions I'm proud of&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval first, generation second.&lt;/strong&gt; Because the comfort comes from real messages, a small model is enough, and it can't invent a memory it was never given.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety runs before the model.&lt;/strong&gt; Crisis checks use keywords plus an optional model check, and a crisis match never reaches the model. The response is fixed and human-written.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contributors are never blocked.&lt;/strong&gt; I first screened loving notes from friends with the same filter, which falsely flagged them. I fixed it so the filter protects what my friend types, and only flags contributor notes for my review.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Honest limitations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 3B-parameter model is less fluent than the biggest closed models, especially in [Hinglish/Hindi, if relevant].&lt;/li&gt;
&lt;li&gt;A keyword safety filter is a minimum safeguard, not a guarantee.&lt;/li&gt;
&lt;li&gt;It's only as good as the messages people record, and it can't replace real support.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;I couldn't have built this the same way with a closed API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy:&lt;/strong&gt; my friend's most vulnerable moments are about as personal as data gets. With an open-weight model on my own laptop, none of it is sent to a third-party server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; it costs nothing per message, so there's no usage bill and no subscription that could disappear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control:&lt;/strong&gt; I picked the model by testing several, wrote the system prompts myself, and wrote the safety rules myself instead of inheriting a vendor's policy. When the safety filter wrongly blocked a loving note, I could open the code, find out why and fix it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It keeps working:&lt;/strong&gt; it runs offline, and the messages and voice notes stay in a plain SQLite file and a folder I can back up. If any company changes its terms tomorrow, my friend's letters are still there.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
