<?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: Kumuda ⚔️</title>
    <description>The latest articles on DEV Community by Kumuda ⚔️ (@kumuda).</description>
    <link>https://dev.to/kumuda</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%2F4166419%2F960d5bac-ecc7-4dcc-a7f5-c3ca9c806e4b.jpg</url>
      <title>DEV Community: Kumuda ⚔️</title>
      <link>https://dev.to/kumuda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kumuda"/>
    <language>en</language>
    <item>
      <title>Marigold /router - A Private Ofrenda for the Memories We Refuse to Lose</title>
      <dc:creator>Kumuda ⚔️</dc:creator>
      <pubDate>Fri, 30 Oct 2026 14:01:01 +0000</pubDate>
      <link>https://dev.to/kumuda/marigold-router-a-private-ofrenda-for-the-memories-we-refuse-to-lose-2e68</link>
      <guid>https://dev.to/kumuda/marigold-router-a-private-ofrenda-for-the-memories-we-refuse-to-lose-2e68</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Some memories are not dramatic enough to become history.&lt;/p&gt;

&lt;p&gt;They are a cup warmed before tea is poured.&lt;/p&gt;

&lt;p&gt;A song playing from an old radio at dusk.&lt;/p&gt;

&lt;p&gt;A yellow sari in a garden.&lt;/p&gt;

&lt;p&gt;The exact way someone said, “Stay a little longer.”&lt;/p&gt;

&lt;p&gt;These details are easy to lose because they are ordinary. But ordinary details are often where a person continues to exist in our lives.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Marigold /router&lt;/strong&gt;: an offline-first oral-history studio and virtual ofrenda for collecting family memories, photographs, recipes, and voice notes.&lt;/p&gt;

&lt;p&gt;It is a place to keep what matters without turning a loved one into a chatbot.&lt;/p&gt;

&lt;p&gt;The central idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Marigold remembers what you give it. It refuses to invent what you did not.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The project was inspired by the challenge theme, &lt;em&gt;Ghost in the Machine&lt;/em&gt;, and by the tension between memory and artificial intelligence.&lt;/p&gt;

&lt;p&gt;A machine can search a memory.&lt;/p&gt;

&lt;p&gt;It can help organise a memory.&lt;/p&gt;

&lt;p&gt;It can transcribe a voice recording.&lt;/p&gt;

&lt;p&gt;But it should not pretend to become the person in the recording.&lt;/p&gt;

&lt;p&gt;That boundary is the heart of Marigold.&lt;/p&gt;

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

&lt;p&gt;💻 &lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/kumudasrip/marigold-router" rel="noopener noreferrer"&gt;https://github.com/kumudasrip/marigold-router&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🎬 &lt;strong&gt;Demo:&lt;/strong&gt; &lt;a href="https://youtu.be/t74MdHNO8nM?si=TRmUi6bRflMOHTtK" rel="noopener noreferrer"&gt;https://youtu.be/t74MdHNO8nM?si=TRmUi6bRflMOHTtK&lt;/a&gt;&lt;br&gt;
Marigold currently runs locally because privacy is part of the product, not just a deployment limitation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Run the browser app
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://github.com/kumudasrip/marigold-router.git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;marigold-router&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http.server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;4173&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Open:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://localhost:4173" rel="noopener noreferrer"&gt;http://localhost:4173&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The archive works without an account, database, or cloud service.&lt;/p&gt;

&lt;p&gt;For the full AI experience, install Ollama and pull the local Gemma model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gemma4:e4b&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The optional Faster-Whisper service enables real audio-to-text transcription.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Marigold Can Do
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Build a private memory archive
&lt;/h3&gt;

&lt;p&gt;You can add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Written stories&lt;/li&gt;
&lt;li&gt;Photographs&lt;/li&gt;
&lt;li&gt;Voice notes&lt;/li&gt;
&lt;li&gt;Years and dates&lt;/li&gt;
&lt;li&gt;Sources and family context&lt;/li&gt;
&lt;li&gt;Thematic tags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Memories are stored in the browser's local storage. There is no account system and no required cloud database.&lt;/p&gt;

&lt;p&gt;The archive includes search, filters, a timeline, and a memory detail drawer so that small fragments can be revisited later.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create an ofrenda-like memory space
&lt;/h3&gt;

&lt;p&gt;I wanted the interface to feel like a place of keeping rather than another dashboard.&lt;/p&gt;

&lt;p&gt;The design uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Marigold orange&lt;/li&gt;
&lt;li&gt;Candle yellow&lt;/li&gt;
&lt;li&gt;Terracotta&lt;/li&gt;
&lt;li&gt;Deep altar red&lt;/li&gt;
&lt;li&gt;Parchment-inspired surfaces&lt;/li&gt;
&lt;li&gt;A hand-crafted Marigold /router logo&lt;/li&gt;
&lt;li&gt;Decorative details inspired by papel picado and altar arrangements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The visual language is intentionally warm. The archive should feel calm enough to stay with a memory for a while.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Ask the Ofrenda Guide
&lt;/h3&gt;

&lt;p&gt;The Guide lets you ask questions about the archive.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What did they love to cook?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the archive contains a relevant memory, the Guide retrieves it and points to the source.&lt;/p&gt;

&lt;p&gt;If the archive does not contain the answer, it says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“That memory is not in the archive yet. I won't fill the silence with a guess.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the most important behaviour in the application.&lt;/p&gt;

&lt;p&gt;A convincing answer is not always a responsible answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Transcribe voice notes locally
&lt;/h3&gt;

&lt;p&gt;A voice note can contain details that someone might never sit down to type.&lt;/p&gt;

&lt;p&gt;Marigold optionally uses Faster-Whisper through a small local FastAPI service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Audio file
    ↓
Local Faster-Whisper service
    ↓
Transcript
    ↓
Memory saved in the browser archive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The audio is sent to &lt;code&gt;127.0.0.1&lt;/code&gt;, not to a hosted transcription provider.&lt;/p&gt;

&lt;p&gt;The transcript is attached to the saved memory, while the original audio remains available locally in the browser archive.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Export the archive
&lt;/h3&gt;

&lt;p&gt;The archive can be exported as a standalone HTML file.&lt;/p&gt;

&lt;p&gt;The exported memorial includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory titles&lt;/li&gt;
&lt;li&gt;Written details&lt;/li&gt;
&lt;li&gt;Dates&lt;/li&gt;
&lt;li&gt;Sources&lt;/li&gt;
&lt;li&gt;Tags&lt;/li&gt;
&lt;li&gt;Uploaded photographs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is to make the archive portable without requiring a Marigold server.&lt;/p&gt;

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

&lt;p&gt;Marigold is intentionally built with a small, understandable stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTML&lt;/strong&gt; for the application structure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS&lt;/strong&gt; for the responsive ofrenda-inspired design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vanilla JavaScript&lt;/strong&gt; for archive interactions and local state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser localStorage&lt;/strong&gt; for private memory persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ollama&lt;/strong&gt; for local model inference&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4 e4b&lt;/strong&gt; for curation and grounded Guide responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster-Whisper&lt;/strong&gt; for local multilingual transcription&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt; for the optional local transcription service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uvicorn&lt;/strong&gt; for running the Whisper service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no required npm build step and no Marigold backend.&lt;/p&gt;

&lt;p&gt;The main app communicates with Ollama directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Marigold browser app
        │
        ├── localStorage
        │
        ├── Ollama
        │     └── Gemma 4 e4b
        │
        └── FastAPI localhost service
              └── Faster-Whisper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where the AI Actually Earns Its Place
&lt;/h2&gt;

&lt;p&gt;I did not want to add AI just because the challenge had “AI” in the title.&lt;/p&gt;

&lt;p&gt;The local model has real, limited responsibilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Curating themes
&lt;/h3&gt;

&lt;p&gt;The Curator reads archive context and suggests thematic threads such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kitchen&lt;/li&gt;
&lt;li&gt;Garden&lt;/li&gt;
&lt;li&gt;Music&lt;/li&gt;
&lt;li&gt;Ritual&lt;/li&gt;
&lt;li&gt;Belonging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are suggestions, not facts about a person.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Grounded retrieval
&lt;/h3&gt;

&lt;p&gt;The Ofrenda Guide receives the archive context and the user's question.&lt;/p&gt;

&lt;p&gt;It is instructed to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answer only from the archive&lt;/li&gt;
&lt;li&gt;Never impersonate the deceased&lt;/li&gt;
&lt;li&gt;Never invent missing details&lt;/li&gt;
&lt;li&gt;Return source references&lt;/li&gt;
&lt;li&gt;Refuse unsupported questions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The frontend also keeps a deterministic fallback retrieval mode so the app remains useful when Ollama is offline.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Transcript cleanup
&lt;/h3&gt;

&lt;p&gt;When a transcript is pasted into the AI Studio, Gemma can help clean punctuation and paragraph breaks while preserving the original meaning.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Audio transcription
&lt;/h3&gt;

&lt;p&gt;Faster-Whisper handles actual voice-to-text conversion locally.&lt;/p&gt;

&lt;p&gt;This is especially useful because oral history is often easier to speak than to type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Gemma and Local Inference?
&lt;/h2&gt;

&lt;p&gt;A cloud model would have made the first demo simpler.&lt;/p&gt;

&lt;p&gt;But a memory archive is exactly the kind of application where sending everything to a remote API should not be the default.&lt;/p&gt;

&lt;p&gt;Using Ollama and Gemma makes the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More private&lt;/li&gt;
&lt;li&gt;Usable without an AI subscription&lt;/li&gt;
&lt;li&gt;More transparent&lt;/li&gt;
&lt;li&gt;Easier to run offline&lt;/li&gt;
&lt;li&gt;Easier to inspect and modify&lt;/li&gt;
&lt;li&gt;Independent of changing hosted API limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model can be replaced locally without changing the product architecture.&lt;/p&gt;

&lt;p&gt;The user controls where inference happens.&lt;/p&gt;

&lt;p&gt;That is important when the data includes family stories, grief, cultural history, or private recordings.&lt;/p&gt;

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

&lt;p&gt;Open innovation matters here because the project depends on trust.&lt;/p&gt;

&lt;p&gt;A closed API might give a polished answer, but it would make it harder to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where the answer came from&lt;/li&gt;
&lt;li&gt;What data left the device&lt;/li&gt;
&lt;li&gt;Which model produced the response&lt;/li&gt;
&lt;li&gt;What happens when the model is wrong&lt;/li&gt;
&lt;li&gt;Whether the service will remain available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With local, open tools, the system is more inspectable.&lt;/p&gt;

&lt;p&gt;Someone can see that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memories are stored in browser storage&lt;/li&gt;
&lt;li&gt;Ollama is called through a local endpoint&lt;/li&gt;
&lt;li&gt;Faster-Whisper runs on localhost&lt;/li&gt;
&lt;li&gt;The Guide prompt explicitly prohibits impersonation&lt;/li&gt;
&lt;li&gt;Unsupported answers fall back to a refusal&lt;/li&gt;
&lt;li&gt;The export is generated locally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open innovation made it possible to build a project where privacy is not a marketing sentence. It is part of the implementation.&lt;/p&gt;

&lt;p&gt;It also made experimentation accessible. I could combine a static frontend, Ollama, Gemma, Faster-Whisper, and FastAPI without needing a paid infrastructure stack or a large production backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned While Building It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Local AI is not automatically responsible AI
&lt;/h3&gt;

&lt;p&gt;Running a model locally improves privacy, but it does not automatically make the output trustworthy.&lt;/p&gt;

&lt;p&gt;The important work was defining what the model should refuse to do.&lt;/p&gt;

&lt;p&gt;The Guide is not allowed to recreate a person or confidently fill in missing history. The refusal behaviour is as important as the successful answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Small models need clear boundaries
&lt;/h3&gt;

&lt;p&gt;A local model performs better when the prompt is narrow and the expected output is explicit.&lt;/p&gt;

&lt;p&gt;For curation, Marigold asks for structured JSON.&lt;/p&gt;

&lt;p&gt;For the Guide, it provides numbered archive entries and expects citation indexes.&lt;/p&gt;

&lt;p&gt;For transcript cleanup, it asks the model to preserve every word and meaning.&lt;/p&gt;

&lt;p&gt;The more specific the task, the more useful the local model becomes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fallbacks matter
&lt;/h3&gt;

&lt;p&gt;Ollama may not be running.&lt;/p&gt;

&lt;p&gt;Whisper may not be installed.&lt;/p&gt;

&lt;p&gt;A model may take too long to respond.&lt;/p&gt;

&lt;p&gt;The archive should still open, search, save memories, and provide honest fallback behaviour.&lt;/p&gt;

&lt;p&gt;AI is an enhancement to the archive, not the foundation that everything else depends on.&lt;/p&gt;

&lt;h3&gt;
  
  
  The UI is part of the emotional design
&lt;/h3&gt;

&lt;p&gt;A memory archive cannot feel like an admin panel.&lt;/p&gt;

&lt;p&gt;The visual design needed to communicate care before the user ever clicked an AI button.&lt;/p&gt;

&lt;p&gt;The warm palette, altar-inspired layout, candle details, and marigold branding are all part of making the project feel like a place people want to return to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Responsible AI Decisions
&lt;/h2&gt;

&lt;p&gt;Marigold follows a few simple rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do not impersonate the deceased.&lt;/li&gt;
&lt;li&gt;Do not fabricate memories.&lt;/li&gt;
&lt;li&gt;Do not present guesses as family history.&lt;/li&gt;
&lt;li&gt;Show where retrieved information came from.&lt;/li&gt;
&lt;li&gt;Say when information is missing.&lt;/li&gt;
&lt;li&gt;Keep the data local by default.&lt;/li&gt;
&lt;li&gt;Use synthetic or consented data for demonstrations.&lt;/li&gt;
&lt;li&gt;Treat browser storage as convenient local persistence, not encryption.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is not trying to make a ghost.&lt;/p&gt;

&lt;p&gt;It is trying to make the silence around a missing memory honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spirit of It
&lt;/h2&gt;

&lt;p&gt;The moment that stayed with me was not the model producing a clever answer.&lt;/p&gt;

&lt;p&gt;It was the refusal.&lt;/p&gt;

&lt;p&gt;I asked the Guide something that was not actually present in the archive, and instead of inventing a warm-sounding response, it said that the memory was not there yet.&lt;/p&gt;

&lt;p&gt;That felt more meaningful than a perfect answer.&lt;/p&gt;

&lt;p&gt;Because grief already contains enough imagined conversations.&lt;/p&gt;

&lt;p&gt;A tool for remembering should not pretend that it can bring someone back.&lt;/p&gt;

&lt;p&gt;It should help us hold on to what was actually said, photographed, recorded, cooked, sung, and lived.&lt;/p&gt;

&lt;p&gt;That is what Marigold /router is for:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A record of love, not a reconstruction.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Best Use of Gemma&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;a href="https://github.com/kumudasrip/marigold-router" rel="noopener noreferrer"&gt;https://github.com/kumudasrip/marigold-router&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository includes setup instructions for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The static browser app&lt;/li&gt;
&lt;li&gt;Ollama and Gemma 4 e4b&lt;/li&gt;
&lt;li&gt;Faster-Whisper&lt;/li&gt;
&lt;li&gt;The local FastAPI transcription service&lt;/li&gt;
&lt;li&gt;Privacy and troubleshooting notes&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>gemma</category>
    </item>
    <item>
      <title>I Built a Pixel Hero That Nags Me to Drink Water (and Runs 100% on My Laptop)</title>
      <dc:creator>Kumuda ⚔️</dc:creator>
      <pubDate>Fri, 23 Oct 2026 13:30:44 +0000</pubDate>
      <link>https://dev.to/kumuda/i-built-a-pixel-hero-that-nags-me-to-drink-water-and-runs-100-on-my-laptop-2ilb</link>
      <guid>https://dev.to/kumuda/i-built-a-pixel-hero-that-nags-me-to-drink-water-and-runs-100-on-my-laptop-2ilb</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Be honest: when did you last drink water while working?&lt;/p&gt;

&lt;p&gt;I can sit at my laptop for four hours straight, and the only thing that moves is my cursor. My eyes burn, my back hurts, and the water bottle next to me is exactly as full as it was in the morning.&lt;/p&gt;

&lt;p&gt;I've tried reminder apps. They all fail the same way: they say the same boring sentence every time, and within a day my brain learns to skip it. A notification I've read 50 times is not a notification anymore, it's wallpaper.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Break Buddy&lt;/strong&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%2Fuif36j1wi695wiqov9za.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%2Fuif36j1wi695wiqov9za.png" alt="Break Buddy" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Break Buddy is a Windows app that runs quietly in the background. When it's time for a break, a pixel-art hero pops up in the corner of my screen, points at an RPG-style dialogue box, and types out a message that is &lt;strong&gt;different every single time&lt;/strong&gt;, in whatever personality I picked. Here's one it wrote for me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Are your eyelids auditioning for an endurance sport?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I didn't write that. I never will see the same reminder twice. And honestly, it works. I laughed, then I actually rested my eyes.&lt;/p&gt;

&lt;p&gt;Here's what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Setup is one typed sentence.&lt;/strong&gt; I typed &lt;em&gt;"remind me to drink water every 45 min, rest my eyes every 20, stretch every hour, nothing before 9am or after 8pm, sarcastic friend tone"&lt;/em&gt; and a local model turned it into a config file. No settings screen, no sliders, no dropdowns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any personality you like.&lt;/strong&gt; Sarcastic friend, calm coach, pirate, angry gym bro. Change one word and the whole app talks differently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It knows when &lt;em&gt;not&lt;/em&gt; to bother you.&lt;/strong&gt; It reads my active window title, labels what I'm doing (coding, video, meeting, documents...), and holds reminders back during a Zoom or Google Meet call. When I leave the call, I get &lt;strong&gt;one&lt;/strong&gt; combined popup instead of three stacked ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-break nudges.&lt;/strong&gt; After 2 hours of continuous screen time, it tells me to take a proper break and mentions what I've mostly been doing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snooze, pause and quit&lt;/strong&gt; from the popup and a system tray icon.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It starts with Windows and has no console window. I set it up once and never touched it again. That's the whole point. 🦥&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&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%2Ffmrttqxehoi4y608e4mu.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%2Ffmrttqxehoi4y608e4mu.png" alt="Eye rest notification 1" width="799" height="499"&gt;&lt;/a&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%2Fo7i99l33vw7m22gvqzqq.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%2Fo7i99l33vw7m22gvqzqq.png" alt="Eye rest notification 2" width="800" height="500"&gt;&lt;/a&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%2F4zlvcn75yo85m3mrcvyb.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%2F4zlvcn75yo85m3mrcvyb.png" alt="Drink water notification" width="800" height="500"&gt;&lt;/a&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%2F5fibne7pj58k5rdngbjh.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%2F5fibne7pj58k5rdngbjh.png" alt="Stretch + water notification" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/kumudasrip/Break-Buddy" rel="noopener noreferrer"&gt;https://github.com/kumudasrip/Break-Buddy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The README has the full setup. You need Windows, Python and &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, and it's about five minutes from clone to first popup.&lt;/p&gt;

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

&lt;p&gt;I know Python well, but I had &lt;strong&gt;never used an open-weight model before this week&lt;/strong&gt;. I had Ollama installed and a few models pulled, and that was it. No fine-tuning, no GPU tricks, just calling a local model from Python and seeing what happens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The stack&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ollama&lt;/strong&gt; runs the models on my machine. I call it from Python like any other library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gemma4:e4b&lt;/strong&gt; is the default model. It's small enough to stay loaded all day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pydantic + Ollama structured output&lt;/strong&gt; forces the setup step to return valid JSON, which is validated and retried up to 3 times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pygetwindow&lt;/strong&gt; reads the active window title, and the Windows &lt;code&gt;GetLastInputInfo&lt;/code&gt; call measures idle time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tkinter&lt;/strong&gt; draws the popups and &lt;strong&gt;pystray&lt;/strong&gt; draws the tray icon.&lt;/li&gt;
&lt;li&gt;The hero is &lt;strong&gt;drawn entirely by Python&lt;/strong&gt; (&lt;code&gt;make_sprite.py&lt;/code&gt;). There are no image files to download. It's an original hooded, caped guardian with a crescent-moon emblem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where the AI actually earns its place&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A plain "every 45 minutes" timer needs no AI at all, and I didn't want the model to be decoration. So it has three real jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Understand me.&lt;/strong&gt; Turn a typed sentence into a structured config.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand my screen.&lt;/strong&gt; Label what I'm doing from the window title, so reminders can wait during meetings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write the message.&lt;/strong&gt; A fresh line for every reminder, in my chosen tone. It only receives a label like "coding" and a number of minutes, never the real window title.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What went wrong (the part I actually learned from)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The model ignored my tone.&lt;/strong&gt;&lt;br&gt;
I typed "sarcastic friend" and the config kept saving &lt;code&gt;"friendly"&lt;/code&gt;. Again and again. The cause was my own schema: I had pre-filled &lt;code&gt;"friendly"&lt;/code&gt; as a default value, and the small model copied the default instead of reading my sentence. The fix was to make the field required with no default, and to add one example ("pirate voice" becomes "pirate"). After that it worked every time. Small models follow defaults over instructions, and now I know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The popups overlapped.&lt;/strong&gt;&lt;br&gt;
I had guessed the popup height, and on my high-DPI screen the real windows were taller. Popups piled on top of each other and hid the one below. I switched to measuring each popup after its text is laid out, and turned on DPI awareness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Meeting detection was too trusting.&lt;/strong&gt;&lt;br&gt;
I tested it with a file named &lt;code&gt;Zoom meeting.txt&lt;/code&gt;, and the model correctly said it was &lt;em&gt;not&lt;/em&gt; a meeting. But that test made me realise something uncomfortable: if the model ever misreads a real call, the app interrupts me in the middle of it, and that's the worst possible failure. So I added fixed patterns for Zoom, Meet, Teams and Webex that run &lt;em&gt;first&lt;/em&gt;, with the model as the backup. In my logs the model caught titles my patterns missed (like &lt;code&gt;Kumuda Sri Padmanabhuni's Zoom Meeting&lt;/code&gt;), so the two layers cover each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Limits I know about.&lt;/strong&gt;&lt;br&gt;
Zoom's home screen ("Zoom Workplace") also holds reminders back, even when I'm not in a call. And a "Connecting…" window at the start of a call can let a reminder slip through for a few seconds. I'd rather tell you than hide it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Slow and steady: which model should an always-on app use?
&lt;/h3&gt;

&lt;p&gt;I wanted to know if I even needed a big model, so I wrote &lt;code&gt;benchmark.py&lt;/code&gt;. It imports the app's own prompts and runs the same tasks through three models: 10 typed setup sentences, 36 window titles (including my real ones), and reminder messages in five tones. Setup and labeling ran at temperature 0 with one attempt per case. The real app retries setup up to 3 times, so real-world results are a little better.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;gemma4:e4b&lt;/th&gt;
&lt;th&gt;gemma4:26b&lt;/th&gt;
&lt;th&gt;qwen3.6:35b&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup: perfect parses&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Window labels (patterns + model)&lt;/td&gt;
&lt;td&gt;97%&lt;/td&gt;
&lt;td&gt;97%&lt;/td&gt;
&lt;td&gt;92%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meeting recall&lt;/td&gt;
&lt;td&gt;7/7&lt;/td&gt;
&lt;td&gt;7/7&lt;/td&gt;
&lt;td&gt;7/7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-meetings called meetings&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messages that follow the rules&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold start&lt;/td&gt;
&lt;td&gt;5.8 s&lt;/td&gt;
&lt;td&gt;64.7 s&lt;/td&gt;
&lt;td&gt;25.7 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Median time per message&lt;/td&gt;
&lt;td&gt;0.2 s&lt;/td&gt;
&lt;td&gt;0.4 s&lt;/td&gt;
&lt;td&gt;0.5 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tokens per second&lt;/td&gt;
&lt;td&gt;118&lt;/td&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;td&gt;61&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory while loaded&lt;/td&gt;
&lt;td&gt;3.3 GB&lt;/td&gt;
&lt;td&gt;18.3 GB&lt;/td&gt;
&lt;td&gt;23.3 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Share running on my GPU&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;51%&lt;/td&gt;
&lt;td&gt;41%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What the numbers told me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The small model was enough.&lt;/strong&gt; The bigger models got exactly one more setup sentence right (e4b dropped the "walk every 90 minutes" reminder from &lt;em&gt;"take a walk every 90 minutes, no reminders before noon, motivational speaker"&lt;/em&gt;). On labeling and message quality, e4b matched or beat them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Going big costs a lot.&lt;/strong&gt; gemma4:26b needs 18 GB and took over a minute to cold-start. On my laptop it only half fit on the GPU. For something that sits in memory all day, a 3.3 GB model running fully on the GPU is the obvious choice. That's the sloth way: do the job with the least energy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The biggest model wasn't the best one.&lt;/strong&gt; qwen3.6:35b labeled my Copilot window as "coding", and one sarcastic message wasn't about water at all: &lt;em&gt;"I suppose someone has to remind you to stay alive today."&lt;/em&gt; Funny, but not a water reminder. 😅&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety rules beat model size.&lt;/strong&gt; All three models caught every meeting and none raised a false alarm, partly thanks to the fixed patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honest caveats.&lt;/strong&gt; This is a small test (10 sentences, 36 titles), I wrote the correct labels myself, and some "mistakes" are debatable. A Chrome tab I'd call "other" was labeled "browsing" by all three models.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I'll say it plainly: ChatGPT or Claude could write a nicer reminder than my local model, and would probably label odd window titles better. If this were a one-off task, I'd use one.&lt;/p&gt;

&lt;p&gt;But Break Buddy isn't a one-off task. It's a tool that watches my screen and runs every day for the rest of my life. For &lt;em&gt;that&lt;/em&gt;, open and local wasn't a nice-to-have. It's the only version I'd actually be willing to run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. It keeps my screen off someone else's server.&lt;/strong&gt;&lt;br&gt;
Break Buddy reads my active window title every few seconds. That's every tab I open, every file I touch, every chat window, every job search, every bank page. I would never stream that to a server I don't control, and I'd be uncomfortable even with a promise not to store it. Because the model runs on my laptop, I could build the one feature that makes the app smart, knowing what I'm doing, with zero privacy cost. With a closed API, that feature would have been the part I couldn't ship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. An always-on tool should cost nothing to leave on.&lt;/strong&gt;&lt;br&gt;
This app makes model calls all day, every day. On a metered API, "set it once and forget it" turns into a bill that never stops. Locally it's free, and &lt;code&gt;gemma4:e4b&lt;/code&gt; idles at 3.3 GB without me noticing. Nothing in the app needs the internet once the model is downloaded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. I could measure instead of guess.&lt;/strong&gt;&lt;br&gt;
Changing one line let me put three models through the same tests and compare accuracy, speed and memory. That's how I discovered that the small model was enough, and that the biggest one wasn't better. With a closed API, I'd be using whatever model the provider picks, and I couldn't have measured the memory, the GPU share or the cold start at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. I could control and debug the model's behavior.&lt;/strong&gt;&lt;br&gt;
When the model kept saving "friendly" instead of "sarcastic friend", I could look at my own schema, set the temperature to 0, switch thinking off, force the output into JSON and find the real cause. When the vision model failed, I could test it from the command line to prove the problem wasn't my code. Having every layer in my hands is what turned "it doesn't work" into "here's why".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Nobody can change it under me.&lt;/strong&gt;&lt;br&gt;
The app behaves the same way tomorrow as it does today. A provider can't quietly update a model, change its pricing, or retire the endpoint my daily tool depends on.&lt;/p&gt;

&lt;p&gt;The honest trade-off is quality on unusual cases. A big cloud model would probably win there. But for this job, a small model on my own machine did everything I needed, and that's the point. Open models let me pick the right size for the problem instead of renting the biggest one by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&lt;/h2&gt;

&lt;p&gt;I set Break Buddy up &lt;strong&gt;once&lt;/strong&gt;, with a single sentence. Now it starts when I turn on my laptop, and since I switched to real mode the reminders show up on schedule without me opening a settings screen or touching a config file.&lt;/p&gt;

&lt;p&gt;And the best part: it actually changed what I did. I really did drink the water, rest my eyes and stand up when the hero told me to. A normal timer I would have dismissed without reading. A pixel hero that roasts me in a fresh sentence every time is much harder to ignore. 😄&lt;/p&gt;

&lt;p&gt;In my Meet and Zoom tests, reminders were held back for the whole call, and I got a single combined popup afterwards instead of a pile of them. So the app doesn't just remind me. It knows when to stay quiet, which is what a lazy person really wants from an assistant.&lt;/p&gt;

&lt;p&gt;Effort I save: no remembering, no setup screens, no switching apps, no dismissing the same sentence 50 times. Effort I spend: one typed sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma.&lt;/strong&gt; The default model is &lt;code&gt;gemma4:e4b&lt;/code&gt;, and I benchmarked it against &lt;code&gt;gemma4:26b&lt;/code&gt; for setup parsing, window labeling, message quality, speed and memory. The small Gemma turned out to be the right model for an always-on tool.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Look away from the screen. Drink some water. The hero is watching.&lt;/em&gt; 👀😂&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Built BirdLens, an AI-Powered Bird Identifier That Encourages You to Look Up</title>
      <dc:creator>Kumuda ⚔️</dc:creator>
      <pubDate>Sat, 10 Oct 2026 13:07:22 +0000</pubDate>
      <link>https://dev.to/kumuda/i-built-birdlens-an-ai-powered-bird-identifier-that-encourages-you-to-look-up-5a7i</link>
      <guid>https://dev.to/kumuda/i-built-birdlens-an-ai-powered-bird-identifier-that-encourages-you-to-look-up-5a7i</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;Here's a small question I've been thinking about: how many interesting things do we walk past every day without really noticing them?&lt;/p&gt;

&lt;p&gt;A bird sitting on a tree. A flash of blue across a park. A bird with a call we've heard a hundred times but never identified.&lt;/p&gt;

&lt;p&gt;We see these things, maybe get curious for a few seconds, and then move on.&lt;/p&gt;

&lt;p&gt;Sometimes, we want to learn more, but identifying an unfamiliar bird isn't always easy. You might not know where to start, and searching through pictures of similar-looking species can be frustrating.&lt;/p&gt;

&lt;p&gt;That's where BirdLens comes in💟&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BirdLens is a web application that helps people explore birdlife through AI-assisted identification, a personal field journal, and small outdoor discovery challenges.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The idea is simple: upload a photograph of a bird, explore the AI's possible identification, and save the moment in your field journal.&lt;/p&gt;

&lt;p&gt;It's not meant to turn birdwatching into another activity where you spend an hour staring at your phone. I want the technology to help you become curious about what's around you, then give you a reason to look away from the screen and observe a little more.&lt;/p&gt;

&lt;p&gt;That felt like a natural fit for this week's &lt;em&gt;Touch Grass&lt;/em&gt; theme.&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%2Fd3eogb2mqisvkgl91mrw.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%2Fd3eogb2mqisvkgl91mrw.png" alt="Landing page" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet BirdLens
&lt;/h2&gt;

&lt;p&gt;You can try the live application here:&lt;/p&gt;

&lt;p&gt;🌿 &lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://birdlens-7vm7.onrender.com/" rel="noopener noreferrer"&gt;https://birdlens-7vm7.onrender.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/kumudasrip/BirdLens" rel="noopener noreferrer"&gt;https://github.com/kumudasrip/BirdLens&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted the experience to feel approachable rather than like a complicated machine-learning demo. You shouldn't need to understand image classification to get started.&lt;/p&gt;

&lt;p&gt;The interface follows a simple flow: upload a photo, explore the results, and save your discovery.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Give a bird a name
&lt;/h3&gt;

&lt;p&gt;The main feature is AI-assisted bird identification.&lt;/p&gt;

&lt;p&gt;Upload a bird photograph, and BirdLens sends it to a hosted image-classification model. The application displays possible species matches along with their model scores.&lt;/p&gt;

&lt;p&gt;For example, while testing BirdLens with a peacock photograph, the model returned &lt;strong&gt;Peacock with a 96.9% model score&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Seeing the result appear in my own application was a particularly satisfying moment. It wasn't just a model running in a separate demo anymore. I had connected the inference process to my own frontend and backend, and the result was appearing in the interface I had built.&lt;/p&gt;

&lt;p&gt;Of course, a high model score doesn't guarantee that an identification is correct. The predictions are suggestions, not definitive answers.&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%2Fjw8od345k5q8yibq3p9x.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%2Fjw8od345k5q8yibq3p9x.png" alt="Bird upload" width="799" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Keep a little field journal
&lt;/h3&gt;

&lt;p&gt;I also wanted somewhere for discoveries to go after identification.&lt;/p&gt;

&lt;p&gt;That's why BirdLens has a personal field journal. Each entry can include a bird name, photograph, date, optional location, and personal notes.&lt;/p&gt;

&lt;p&gt;Maybe you spotted a bird in a nearby park. Maybe you noticed its colour, watched it searching for food, or simply liked the way it looked.&lt;/p&gt;

&lt;p&gt;You can record those details and build a small collection of sightings over time.&lt;/p&gt;

&lt;p&gt;The journal also displays the total number of sightings and the number of different species recorded. Saved entries appear in a responsive card layout, making it easier to revisit them later.&lt;/p&gt;

&lt;p&gt;The journal uses browser local storage, so there's no account creation or database setup involved. Your saved sightings remain in that browser on that device rather than syncing automatically to an online account.&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%2Fs01mtu8yqu3cuvq73s7y.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%2Fs01mtu8yqu3cuvq73s7y.png" alt="Journal" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Make going outside the point
&lt;/h3&gt;

&lt;p&gt;BirdLens also includes Nature Quests: short, beginner-friendly prompts that encourage you to pay closer attention to your surroundings.&lt;/p&gt;

&lt;p&gt;They might encourage you to notice bird behaviour, look for different colours, or observe what is happening around you.&lt;/p&gt;

&lt;p&gt;These quests currently use predefined prompts rather than AI-generated challenges. I wanted to start with something lightweight that supports the project's main idea: getting outside and noticing things you might otherwise overlook.&lt;/p&gt;

&lt;p&gt;The goal isn't to collect the most sightings or spend the longest time using an app. It's to find a small reason to explore.&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%2Fc2rjpgjb6o07ru5j2w9e.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%2Fc2rjpgjb6o07ru5j2w9e.png" alt="Quests" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;I built BirdLens using a straightforward web stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTML, CSS, JavaScript and Bootstrap 5&lt;/strong&gt; for the frontend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python and Flask&lt;/strong&gt; for the backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hugging Face Spaces and Gradio Client&lt;/strong&gt; for hosted model inference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser local storage&lt;/strong&gt; for the field journal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render&lt;/strong&gt; for deploying the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture is intentionally simple.&lt;/p&gt;

&lt;p&gt;When someone uploads a photograph, the frontend sends it to a Flask API endpoint. The backend forwards the image to my Hugging Face Space, which runs the bird-classification model. The resulting predictions are returned to the frontend and displayed to the user.&lt;/p&gt;

&lt;p&gt;The journal works separately from the AI identification flow. Once the user has a bird name, they can save the sighting locally, including when the AI service is temporarily unavailable.&lt;/p&gt;

&lt;p&gt;This separation became especially useful while I was building and debugging the project.&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%2Fblhkznhw7ck1zg0sqebl.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%2Fblhkznhw7ck1zg0sqebl.png" alt="Architecture" width="800" height="818"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing the AI model
&lt;/h3&gt;

&lt;p&gt;BirdLens currently uses the &lt;code&gt;prithivMLmods/Bird-Species-Classifier-526&lt;/code&gt; image-classification model, accessed through my Hugging Face Space.&lt;/p&gt;

&lt;p&gt;I chose this approach because an image-classification model is a natural fit for the problem. The input is a photograph, and the output is a ranked set of possible species.&lt;/p&gt;

&lt;p&gt;The model is available through an open-weight ecosystem, which gives the project a more flexible starting point than building around a single proprietary vision API.&lt;/p&gt;

&lt;p&gt;I also wanted to keep the inference integration behind a small backend API. That makes the frontend simpler and gives me a place to handle authentication, errors, and future changes to the model provider.&lt;/p&gt;

&lt;p&gt;The current implementation uses hosted inference. It does not run the model directly on the user's device, and it depends on the availability and usage limits of the Hugging Face Space.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part That Didn't Work Immediately
&lt;/h2&gt;

&lt;p&gt;Getting the model to make predictions was only part of the challenge. Getting the entire application to work reliably was another story.&lt;/p&gt;

&lt;p&gt;One issue appeared when I started testing the integration through my website.&lt;/p&gt;

&lt;p&gt;The Hugging Face Space could identify a bird when I used its own interface, but requests from BirdLens sometimes failed with a ZeroGPU usage-limit error.&lt;/p&gt;

&lt;p&gt;At first, it was confusing. The model worked, the image was valid, and the frontend appeared to be doing its job. Yet the application couldn't consistently get a result.&lt;/p&gt;

&lt;p&gt;The important lesson was to stop treating every failure as a frontend problem.&lt;/p&gt;

&lt;p&gt;I checked the Flask terminal logs and traced the request through the application. That helped me distinguish a request reaching the backend from a request successfully completing inference.&lt;/p&gt;

&lt;p&gt;I then configured Hugging Face authentication through an environment variable instead of placing a token in the frontend. After that change, I was able to test multiple bird photographs successfully.&lt;/p&gt;

&lt;p&gt;That wasn't the end of the deployment work, either. The local application and the Render deployment behaved differently because the hosted environment had its own configuration and dependency requirements. Checking the deployment logs helped me identify and fix a Gradio Client configuration error.&lt;/p&gt;

&lt;p&gt;Eventually, the complete flow worked on the live application: upload a photograph, receive predictions, edit the bird name, and save the sighting.&lt;/p&gt;

&lt;p&gt;This was a useful reminder that a successful model demo and a working application are two different things. The integration, error handling, authentication, and deployment all matter.&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%2F4yzetko1g4kd1no9xdfq.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%2F4yzetko1g4kd1no9xdfq.png" alt="Identification" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking BirdLens Outside: From a Nature Quest to a Real Sighting 🌿
&lt;/h2&gt;

&lt;p&gt;I didn't want BirdLens to be just another AI demo that I built, tested in my browser, and left online.&lt;/p&gt;

&lt;p&gt;One of its main features is Nature Quests, so I decided to use the app the way I had imagined someone using it in real life: start with a small challenge, head outside, and see what I discover.&lt;/p&gt;

&lt;p&gt;I opened BirdLens on my phone and generated today's quest:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Listen carefully for two minutes. Can you distinguish different bird calls?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That gave me a reason to pause and pay attention to the birds around me. While observing them, I noticed one that caught my attention because of its unusually long, curved beak. It looked unfamiliar to me, and I wanted to know more about it.&lt;/p&gt;

&lt;p&gt;So I took a photograph and opened BirdLens on my phone.&lt;/p&gt;

&lt;p&gt;I uploaded the image, and the classifier returned &lt;strong&gt;Tropical Kingbird&lt;/strong&gt; as its top prediction, with a model score of &lt;strong&gt;24.7%&lt;/strong&gt;. Other suggestions included Malagasy White Eye and Malachite Kingfisher, with much lower scores.&lt;/p&gt;

&lt;p&gt;The result was interesting, but the relatively low top score also reminded me not to treat the prediction as a confirmed identification. I hadn't independently verified the species, so I kept the result as a suggestion rather than a definitive answer.&lt;/p&gt;

&lt;p&gt;I then saved the sighting in my BirdLens field journal, adding a note about what had caught my attention:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Its beak was so long and it looked very new to me.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That small observation was the reason I wanted to identify the bird in the first place. The AI gave me a starting point, while the field journal let me preserve the moment and my own thoughts about it.&lt;/p&gt;

&lt;h3&gt;
  
  
  My outdoor experiment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nature Quest:&lt;/strong&gt; Listen carefully for two minutes and try to distinguish different bird calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observation:&lt;/strong&gt; A bird with a long, curved beak that I hadn't noticed before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BirdLens prediction:&lt;/strong&gt; Tropical Kingbird — 24.7% model score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field journal:&lt;/strong&gt; One sighting saved, with my observation recorded in the notes.&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%2Fgmm5lym6i2r4xobt7jps.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%2Fgmm5lym6i2r4xobt7jps.jpeg" alt="Nature Quest" width="746" height="1600"&gt;&lt;/a&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%2Fqb8gqlwha5pz60cj5zws.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%2Fqb8gqlwha5pz60cj5zws.jpeg" alt="Identification" width="540" height="1354"&gt;&lt;/a&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%2Fgwhq1bxodlx26516zbgm.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%2Fgwhq1bxodlx26516zbgm.jpeg" alt="Saved it" width="751" height="1600"&gt;&lt;/a&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%2Fzei4xhvbaywcwlo3bvpn.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%2Fzei4xhvbaywcwlo3bvpn.jpeg" alt="My Journal" width="540" height="1377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I liked most was how the experience connected the different parts of BirdLens. A simple quest encouraged me to observe my surroundings, that observation made me curious about a bird, and the AI gave me somewhere to start exploring.&lt;/p&gt;

&lt;p&gt;The prediction itself wasn't conclusive, and that's an important part of the experience too. AI can help us investigate what we see, but it shouldn't replace careful observation or independent verification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BirdLens wasn't just something I built to identify birds. I finally used it for the reason I built it: to turn a little curiosity outdoors into a discovery worth remembering.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Open Innovation Matters Here
&lt;/h2&gt;

&lt;p&gt;This is one of the most interesting parts of building BirdLens for me.&lt;/p&gt;

&lt;p&gt;An AI-powered bird identifier could be built around a proprietary vision API. That might be a perfectly reasonable engineering choice, but it would also tie the application to that provider's model access, API behaviour, pricing, and available features.&lt;/p&gt;

&lt;p&gt;Using an open-weight model gives me another option.&lt;/p&gt;

&lt;p&gt;I can explore how the classifier behaves, evaluate its results on different bird photographs, and investigate alternative models as the project develops. The model is not simply an opaque feature that I have no choice but to accept as-is.&lt;/p&gt;

&lt;p&gt;It also makes experimentation more accessible. I can build the application around a model that is already available, test its capabilities, and decide where it works well and where it needs improvement.&lt;/p&gt;

&lt;p&gt;There is an important distinction, though: &lt;strong&gt;open-weight does not automatically mean offline, free forever, or completely private.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;BirdLens currently sends photographs to a hosted inference service. Its availability depends on that service's usage limits, and the current implementation does not provide local inference.&lt;/p&gt;

&lt;p&gt;I see this as a starting point rather than a limitation I should hide. A future version could investigate local inference, alternative compatible models, or other deployment options. Those changes would need to be evaluated for model quality, hardware requirements, latency, and cost.&lt;/p&gt;

&lt;p&gt;For this version, the main benefit is having a working AI integration built around a model I can investigate and potentially replace, rather than committing the entire application to one closed AI API.&lt;/p&gt;

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

&lt;p&gt;Building BirdLens reminded me that a project doesn't become complete just because its main feature works once.&lt;/p&gt;

&lt;p&gt;I learned how the pieces of an AI-powered web application fit together: the frontend handles the user's interaction, the backend manages requests, the inference service runs the model, and the application needs to handle failures at every boundary.&lt;/p&gt;

&lt;p&gt;I also learned the importance of reading logs rather than guessing. The ZeroGPU issue and the deployment configuration error were different problems, even though both initially appeared to the user as a failed identification.&lt;/p&gt;

&lt;p&gt;On the product side, I found myself thinking about what should happen when AI is unavailable. Rather than blocking the entire experience, BirdLens lets users enter a bird name themselves and still save the sighting. That keeps the field journal useful even when the model cannot provide an answer.&lt;/p&gt;

&lt;p&gt;And perhaps most importantly, I had to think about how the application should encourage people to use it.&lt;/p&gt;

&lt;p&gt;The purpose isn't to keep someone browsing bird cards indefinitely. It's to help them become curious about the birds around them and make that curiosity a reason to spend more time outdoors.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;BirdLens is working as a deployed application, but there is still room to improve it.&lt;/p&gt;

&lt;p&gt;Some things I'd like to explore next include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expanding the educational information available for identified species.&lt;/li&gt;
&lt;li&gt;Testing the classifier across more bird species, photographs, and lighting conditions.&lt;/li&gt;
&lt;li&gt;Improving identification reliability and communicating uncertainty clearly.&lt;/li&gt;
&lt;li&gt;Exploring alternative inference options and their trade-offs.&lt;/li&gt;
&lt;li&gt;Making the experience more accessible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd especially like to evaluate how useful the predictions are with photographs taken in everyday outdoor conditions, rather than relying only on a few successful test images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try BirdLens
&lt;/h2&gt;

&lt;p&gt;🌿 &lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://birdlens-7vm7.onrender.com/" rel="noopener noreferrer"&gt;https://birdlens-7vm7.onrender.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;strong&gt;GitHub repository:&lt;/strong&gt; &lt;a href="https://github.com/kumudasrip/BirdLens" rel="noopener noreferrer"&gt;https://github.com/kumudasrip/BirdLens&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, I'd love to know which bird you tested it with and how well the predictions matched what you expected. Feedback about the identification experience, journal, or outdoor challenges would be especially useful.&lt;/p&gt;

&lt;p&gt;BirdLens is a small project, but I like the idea behind it: technology can help us pay more attention to the world rather than simply giving us another reason to stay online.&lt;/p&gt;

&lt;p&gt;Sometimes, the most interesting discovery is waiting just outside.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Look closer. Discover more.&lt;/strong&gt; 🌿&lt;/p&gt;

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