<?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: Sahan Dilshan</title>
    <description>The latest articles on DEV Community by Sahan Dilshan (@sahan20030814).</description>
    <link>https://dev.to/sahan20030814</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%2F4176381%2F3794f322-b706-442a-b679-b673703fa9fd.jpg</url>
      <title>DEV Community: Sahan Dilshan</title>
      <link>https://dev.to/sahan20030814</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sahan20030814"/>
    <language>en</language>
    <item>
      <title>Heirloom: Turning a Loved One's Rambling Voice Memo into a Family Cookbook (100% Local, with Gemma + Whisper)</title>
      <dc:creator>Sahan Dilshan</dc:creator>
      <pubDate>Mon, 26 Oct 2026 18:57:11 +0000</pubDate>
      <link>https://dev.to/sahan20030814/heirloom-turning-a-loved-ones-rambling-voice-memo-into-a-family-cookbook-100-local-with-gemma-2pkn</link>
      <guid>https://dev.to/sahan20030814/heirloom-turning-a-loved-ones-rambling-voice-memo-into-a-family-cookbook-100-local-with-gemma-2pkn</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;Every family has one person who cooks by feel. Ask [[NAME, e.g. my Seeya]] how much coconut goes into his pol sambol and the answer is "enough." The recipe isn't written anywhere. It lives in his hands.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Heirloom&lt;/strong&gt;. You hit record while they cook and talk, rambling, interrupting themselves, telling you who taught them. Heirloom turns that into a proper recipe page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clean recipe:&lt;/strong&gt; ingredients with amounts, ordered steps, tips, tags and allergens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Their story, kept:&lt;/strong&gt; the memories and "secrets" from the memo, not just the instructions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask them next time:&lt;/strong&gt; when the memo is vague ("a spoon of Maldive fish"), Heirloom does &lt;em&gt;not&lt;/em&gt; guess. It writes a question to take back to them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Their voice, kept:&lt;/strong&gt; the original recording plays inside the recipe page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cooking mode&lt;/strong&gt; (one big step at a time for messy hands), a cookbook shelf, and a &lt;strong&gt;print-to-PDF family book&lt;/strong&gt; you can actually hand over&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The design rule I held to: a recipe app that invents quantities is worse than no app. Uncertainty becomes a conversation with the person, which is the whole point.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://heirloom-saza.onrender.com" rel="noopener noreferrer"&gt;https://heirloom-saza.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A quick honest note: the hosted link runs in &lt;strong&gt;demo mode&lt;/strong&gt;. A free Render instance can't run an LLM, so it returns a canned sample so you can click through the whole experience. The real pipeline (local Whisper + local Gemma) runs on your own machine, and the screenshots below are from that local setup. The free instance also sleeps when idle, so the first load can take about a minute.&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%2F88n8wxju8n21xqbruvxs.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%2F88n8wxju8n21xqbruvxs.png" alt="Record tab: live waveform recorder" width="800" height="1349"&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%2Fchcs3d03r2oyldxj4bor.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%2Fchcs3d03r2oyldxj4bor.png" alt="Upload tab: drop a WhatsApp voice note" width="800" height="1336"&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%2F2gnu09nhus1oha05t2vz.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%2F2gnu09nhus1oha05t2vz.png" alt="Paste tab: type or paste what they told you" width="800" height="1388"&gt;&lt;/a&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/Sahan20030814" rel="noopener noreferrer"&gt;
        Sahan20030814
      &lt;/a&gt; / &lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes" rel="noopener noreferrer"&gt;
        heirloom-voice-recipes
      &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;Heirloom 🍲&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Turn a loved one's rambling voice memo into a beautiful family recipe page, 100% on your own computer, with open-source AI.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Built for the &lt;em&gt;Hacktoberfest 2026 Weekend Challenge: Build for a Friend&lt;/em&gt; (DEV). One real person, one real problem: a grandparent who cooks the best food in the family and has never written a single recipe down.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No account. No API key. No bill. The voice, the stories and the health details of the people you love never touch a server you don't control.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of contents&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#what-it-does" rel="noopener noreferrer"&gt;What it does&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#how-it-works" rel="noopener noreferrer"&gt;How it works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#tech-stack" rel="noopener noreferrer"&gt;Tech stack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#quick-start-local" rel="noopener noreferrer"&gt;Quick start (local)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#configuration" rel="noopener noreferrer"&gt;Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#deploy-on-render" rel="noopener noreferrer"&gt;Deploy on Render&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#api-reference" rel="noopener noreferrer"&gt;API reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#project-structure" rel="noopener noreferrer"&gt;Project structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#design-decisions" rel="noopener noreferrer"&gt;Design decisions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#why-open-source-matters-here" rel="noopener noreferrer"&gt;Why open source matters here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#troubleshooting" rel="noopener noreferrer"&gt;Troubleshooting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#roadmap" rel="noopener noreferrer"&gt;Roadmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Sahan20030814/heirloom-voice-recipes#license" rel="noopener noreferrer"&gt;License&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🎙️ &lt;strong&gt;Capture&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Record in the browser (live waveform), upload a voice note (WhatsApp &lt;code&gt;.opus&lt;/code&gt;, &lt;code&gt;.m4a&lt;/code&gt;, &lt;code&gt;.mp3&lt;/code&gt;, &lt;code&gt;.wav&lt;/code&gt;), or paste text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧠 &lt;strong&gt;Understand&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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/Sahan20030814/heirloom-voice-recipes" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;MIT licensed, with a full README (local setup for Windows/macOS/Linux and a Render guide).&lt;/p&gt;

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

&lt;p&gt;The pipeline: &lt;code&gt;voice memo → faster-whisper (local) → Gemma via Ollama (local) → structured recipe → cookbook&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speech-to-text:&lt;/strong&gt; &lt;a href="https://github.com/SYSTRAN/faster-whisper" rel="noopener noreferrer"&gt;faster-whisper&lt;/a&gt; (CTranslate2, int8, runs on CPU) with voice-activity detection to skip silence and kitchen noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understanding:&lt;/strong&gt; Gemma 3 (open-weight) served by Ollama. I pass a &lt;strong&gt;JSON schema&lt;/strong&gt; in Ollama's &lt;code&gt;format&lt;/code&gt; field, so even a small 4B model returns valid, typed recipe JSON instead of free-form text I'd have to parse and pray over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A prompt that refuses to invent:&lt;/strong&gt; the model may never add ingredients, amounts or times that weren't said. Anything vague goes into an &lt;code&gt;unclear&lt;/code&gt; list, which the UI shows as "Ask them next time." A &lt;code&gt;normalise()&lt;/code&gt; step guarantees the response shape on the way out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App:&lt;/strong&gt; FastAPI backend, and a plain HTML/CSS/JS frontend with no framework, no build step and no CDN (system fonts), so it works fully offline. The recorder uses &lt;code&gt;MediaRecorder&lt;/code&gt; with a live waveform, and a print stylesheet turns the cookbook into a PDF book.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything is one env var away from changing:&lt;/strong&gt; &lt;code&gt;OLLAMA_MODEL&lt;/code&gt;, &lt;code&gt;WHISPER_MODEL&lt;/code&gt;, &lt;code&gt;OLLAMA_URL&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built it pair-programming with Claude as an AI coding assistant, then ran, tested and adjusted it myself.&lt;/p&gt;

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

&lt;p&gt;This project would be a different (and worse) product on a closed API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy is the feature.&lt;/strong&gt; A recording of an elderly relative, plus the allergies and health details they mention while cooking, is exactly what shouldn't be uploaded to a server I don't control. With local Whisper and Gemma, nothing leaves the laptop. There's no account, no terms of service, no retention policy to read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swappable brains.&lt;/strong&gt; Whisper's accuracy on Sinhala and Tamil lags English, so a bigger Whisper size is one variable away. If a better multilingual model appears next month, I change one line and keep every recipe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It outlives pricing pages.&lt;/strong&gt; A family archive should still work in 20 years. Local models cost nothing per memo and work in the kitchen with no signal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The honest trade-off: running locally on CPU is slower than a hosted API, [[add your real timing for one memo, e.g. "about X seconds per 1-minute memo with whisper-small + gemma3:4b"]], and a 4B model needs the "never invent" guardrails to stay trustworthy. For an archive of voices I care about, I'll take slower and private every time.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; Gemma 3 runs fully locally through Ollama and does the core work: turning a messy spoken memo into a structured, honest recipe via schema-constrained output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Render:&lt;/strong&gt; Heirloom's web app is deployed on Render as a free web service, using a &lt;code&gt;render.yaml&lt;/code&gt; Blueprint and a &lt;code&gt;/healthz&lt;/code&gt; health check, and that is the live demo linked above. To be upfront about scope: the hosted instance runs in demo mode (canned AI responses), because a free instance can't run the models. The real Whisper + Gemma pipeline runs locally. The README documents the paid path for running Ollama as a private service on Render, which I haven't deployed.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Bedtime Sloth: a story for tonight, written by Gemma on your own computer</title>
      <dc:creator>Sahan Dilshan</dc:creator>
      <pubDate>Mon, 26 Oct 2026 04:24:03 +0000</pubDate>
      <link>https://dev.to/sahan20030814/bedtime-sloth-a-story-for-tonight-written-by-gemma-on-your-own-computer-6ac</link>
      <guid>https://dev.to/sahan20030814/bedtime-sloth-a-story-for-tonight-written-by-gemma-on-your-own-computer-6ac</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;Bedtime is when parents are most tired and children most want one more story. &lt;strong&gt;Bedtime Sloth&lt;/strong&gt; makes that part lazy. You type a child's name, pick a place, and press one button. It writes a new calm story, reads it slowly, and plays the sounds of the night. All of it runs on your own computer.&lt;/p&gt;

&lt;p&gt;Here is what you can set before you press the button:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Who it is for&lt;/strong&gt;, and an optional favourite thing to include, like "a red scarf and a sleepy owl".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where it happens:&lt;/strong&gt; Forest, Ocean, Space, Gentle dragons, Night trains or Clouds. Each place changes the colour of the night sky.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How long:&lt;/strong&gt; 3, 5 or 8 minutes, and how it should feel (Gentle, Very sleepy, Cozy and warm).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How slowly it is read:&lt;/strong&gt; Slow, Slower or Very slow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The sounds of the night:&lt;/strong&gt; rain, ocean, forest night, fireplace, wind or silence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is built for a dark room. The sounds fade out on a sleep timer, a Night light switches the screen to warm colours, Screen off turns it fully black, and the controls fade away on their own. At the end you can continue the story tomorrow night, save it as text, or delete it. Every story is saved on the computer, so a favourite can be replayed.&lt;/p&gt;

&lt;p&gt;The people who get to be lazier are tired parents, carers and babysitters who do not have to invent a story every night, and anyone who would rather not send a child's name and favourite things to a cloud app.&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%2Fsddl771thmsv7elsj4i5.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%2Fsddl771thmsv7elsj4i5.png" alt="Bedtime Sloth home page: a sloth hanging from a branch under a night sky, with a form for the child's name, the place, the length, the mood and the sounds" width="800" height="851"&gt;&lt;/a&gt;&lt;/p&gt;

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

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

&lt;p&gt;The video shows a full run on my laptop: the settings, Gemma writing a story called "Cloud Dreams", and the night sounds, timer, night light and screen off. The narration I added in the video is synthetic (made with the open-source Piper text-to-speech) and the background music was generated for the video.&lt;/p&gt;

&lt;p&gt;Hosted copy: &lt;a href="https://bedtime-sloth.onrender.com/" rel="noopener noreferrer"&gt;https://bedtime-sloth.onrender.com/&lt;/a&gt; runs on Render's free tier, which cannot run a language model, so it tells a short built-in tale and the browser's voice reads it. The real experience, with Gemma writing the story, is the local one.&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%2Fth6nu5lr2le1le7bkiep.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%2Fth6nu5lr2le1le7bkiep.png" alt="The waiting screen: a breathing moon and the words " width="799" height="386"&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%2F15zzyy2mfreakvsmbvsy.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%2F15zzyy2mfreakvsmbvsy.png" alt="The story player: one paragraph of a bedtime story in large text, with controls for the sounds, the volume and a fade timer" width="799" height="386"&gt;&lt;/a&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/Sahan20030814" rel="noopener noreferrer"&gt;
        Sahan20030814
      &lt;/a&gt; / &lt;a href="https://github.com/Sahan20030814/bedtime-sloth" rel="noopener noreferrer"&gt;
        bedtime-sloth
      &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;Bedtime Sloth&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A bedtime story written for your child tonight, read slowly in a calm voice, with the sounds of the night. It runs on your own computer, so it works with no internet, and the child's name and every story stay on your machine.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Built for the &lt;strong&gt;Hacktoberfest 2026 Open-Source AI Challenge, Week 3&lt;/strong&gt; (theme: &lt;em&gt;Slow and Steady&lt;/em&gt;).&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The problem&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Bedtime is the part of the day when parents are most tired and children most want "one more story". Books run out, repeating the same tale gets dull, and story apps usually need an account, an internet connection and a subscription, and they send your child's name and interests to someone else's server.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What Bedtime Sloth does&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A new story every night&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gemma writes 8, 12 or 18 short paragraphs (about 3, 5 or 8 minutes) from a name, a place, an optional favourite thing and&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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/Sahan20030814/bedtime-sloth" 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;ul&gt;
&lt;li&gt;
&lt;strong&gt;The storyteller:&lt;/strong&gt; Gemma 3 4B (open weights), run locally with Ollama. The system prompt sets the tone: gentle and safe, nothing scary, every small worry solved kindly, slower and quieter towards the end, and the child falls asleep in the last paragraph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The voice:&lt;/strong&gt; each paragraph is read by Piper, an open-source text-to-speech model, and the next one is prepared while the current one plays. If Piper is not installed, the browser's own voice reads the story.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The sounds:&lt;/strong&gt; rain, ocean, forest night, fireplace and wind are made live in the browser with the Web Audio API, from filtered noise and oscillators. There are no sound files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rest:&lt;/strong&gt; one Python standard-library server and one HTML file. No framework, no packages for the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A bug that taught me something.&lt;/strong&gt; My first version asked Gemma for strict JSON. Sometimes a small model answers in a shape you did not plan for, and my app quietly fell back to a built-in tale. I only noticed because the screen said "(built-in story)" on a recording I thought was a Gemma story. The fix was to stop being strict:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the prompt now asks for plain text, a title on the first line and paragraphs separated by blank lines&lt;/li&gt;
&lt;li&gt;the parser accepts plain text, JSON with other key names, markdown, numbered paragraphs, or one long block that it splits into paragraphs&lt;/li&gt;
&lt;li&gt;it retries once with a simpler prompt&lt;/li&gt;
&lt;li&gt;when it still has to fall back, a red banner says why, and &lt;code&gt;python server.py --test&lt;/code&gt; checks the model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tested the parser against a fake model that answered in nine different ways, including a missing model, an out-of-memory error and a refused connection. I have not tried every Gemma answer, so the banner stays.&lt;/p&gt;

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

&lt;p&gt;A bedtime app learns a child's name, what they love and when they go to sleep. With an open-weight model, all of that stays on the computer. The model, the voice and the sounds all run locally, so once the model and the voice are downloaded, nothing in the app needs the internet. A closed API cannot promise that.&lt;/p&gt;

&lt;p&gt;It also changes the cost. A story every night, for years, would normally mean a subscription or a per-call fee. Here the cost of one more story is electricity. I can swap the model with one setting (&lt;code&gt;BS_MODEL&lt;/code&gt;), and in future I could fine-tune a small model on a family's own favourite stories. I have not done that part yet.&lt;/p&gt;

&lt;p&gt;There is a trade-off. A 4B model sometimes writes an odd line or a weak ending, and a prompt cannot guarantee a story is suitable for every child. My advice in the README is to read a story first if the child is sensitive. I would rather say that than pretend the model is perfect.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;I built this with Claude in a chat window, not in a coding-agent tool, so there is no agent session file to embed. I used it for the code, the README and a first draft of this post. I ran the app, found the fallback bug, recorded the demo and decided what to keep.&lt;/p&gt;

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

&lt;p&gt;The effort that disappears is the part at the start of bedtime: thinking of a story, then reading it aloud for five or ten minutes. With Bedtime Sloth it is typing a name and pressing one button. In my screen recording (a 3 minute story on my laptop with 24 GB of RAM), the story appeared about five seconds after I pressed the button. A 3 minute story is 8 short paragraphs.&lt;/p&gt;

&lt;p&gt;Who used it this week: My niece asked for a story about her red scarf, and fell asleep before the last paragraph."&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; Gemma 3 4B runs locally through Ollama and writes every story the app tells.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render:&lt;/strong&gt; The web app and story player are hosted on Render from a Docker blueprint in the repo. The model does not run on Render, so this category is only for the front end.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>ListingLab: a local Gemma model that writes your Etsy and eBay listings and keeps your photos private</title>
      <dc:creator>Sahan Dilshan</dc:creator>
      <pubDate>Sun, 18 Oct 2026 06:56:35 +0000</pubDate>
      <link>https://dev.to/sahan20030814/listinglab-a-local-gemma-model-that-writes-your-etsy-and-ebay-listings-and-keeps-your-photos-bp9</link>
      <guid>https://dev.to/sahan20030814/listinglab-a-local-gemma-model-that-writes-your-etsy-and-ebay-listings-and-keeps-your-photos-bp9</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Writing listings is the part of selling online that eats the time. Every item needs a title that fits the platform's limit, a description, key details and a full set of tags, and then you do it all again for the next product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ListingLab&lt;/strong&gt; is a small web app that writes the first draft. You drop up to four product photos, add a few notes about the item, pick Etsy or eBay, a voice and a language, and it writes a listing you can paste into the marketplace. It runs on your own laptop with Gemma through Ollama, so your photos and notes never leave your machine, and each listing shows &lt;code&gt;$0.00 in API fees&lt;/code&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%2Fveqbrxd8rd3tufcw5pqt.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%2Fveqbrxd8rd3tufcw5pqt.png" alt="ListingLab with four product photos, notes, and a generated eBay listing on a yellow price tag: a title with a 64 of 80 character meter, a description, key details and tags" width="800" height="968"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writes the listing:&lt;/strong&gt; title, a two-paragraph description, key details and tags.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knows the marketplace rules:&lt;/strong&gt; Etsy titles up to 140 characters with up to 13 tags of 20 characters or fewer, eBay titles up to 80 characters. A live meter shows how close the title is to the limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lets you steer it:&lt;/strong&gt; four voices (Warm, Minimal, Playful, Premium), four languages (English, Spanish, French, German), two alternative titles, and Regenerate, Shorter and More detail buttons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checks the result:&lt;/strong&gt; a 0 to 100 score from six listing basics. It is a checklist, not a sales prediction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does the maths:&lt;/strong&gt; a Profit tab turns your item cost, selling price, shipping and fee percentage into profit, margin and break-even price.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keeps a history&lt;/strong&gt; of recent listings, exports to .txt or CSV, and lets you click a photo to enlarge it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who would pay for it?&lt;/strong&gt; My guess is sellers with a growing catalogue, such as small Etsy shops and people reselling items on eBay, for whom time per listing matters and who would rather not send product photos to a cloud service. Because it costs nothing to run per listing, it could be sold for a one-off price or a small monthly fee. I have not tested whether anyone would pay, so please read that as a hypothesis, not a result.&lt;/p&gt;

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

&lt;p&gt;Live: &lt;a href="https://listinglab-15yt.onrender.com/" rel="noopener noreferrer"&gt;https://listinglab-15yt.onrender.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An honest note about the hosted copy: it runs on Render's free tier, which cannot run a language model. There, the app says &lt;em&gt;Model offline, template mode&lt;/em&gt; and builds a plain template from your notes. Everything else works, including the platform limits, the Check tab and the Profit tab. The first load can take up to a minute while the free instance wakes up. The real experience is the local one, where Gemma writes the listing.&lt;/p&gt;

&lt;p&gt;The empty app, with the drop zone, notes box, platform switch, voice and language:&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%2Fpz0kwnp7fhutfegk8m6x.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%2Fpz0kwnp7fhutfegk8m6x.png" alt="ListingLab's empty state: a photo drop zone, a notes box, an Etsy and eBay switch, voice chips and a language menu" width="800" height="702"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Check&lt;/strong&gt; tab scoring a listing:&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%2Fntarso513efqr0vyo8f6.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%2Fntarso513efqr0vyo8f6.png" alt="The Check tab showing a score of 50 out of 100 with a checklist of six listing basics" width="800" height="974"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Profit&lt;/strong&gt; tab:&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%2Fx3lmkd7q58340cki7vnn.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%2Fx3lmkd7q58340cki7vnn.png" alt="The Profit tab: item cost 2000, selling price 3000, shipping 50 and 5 percent fees give a profit of 800, a 27 percent margin and a break-even price of 2157.89" width="800" height="968"&gt;&lt;/a&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/Sahan20030814" rel="noopener noreferrer"&gt;
        Sahan20030814
      &lt;/a&gt; / &lt;a href="https://github.com/Sahan20030814/listinglab" rel="noopener noreferrer"&gt;
        listinglab
      &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;ListingLab&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Photos and a few notes in. A ready-to-paste Etsy or eBay listing out. It runs on the seller's own laptop, so product photos stay private and every listing costs nothing in API fees.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Built for the &lt;strong&gt;Hacktoberfest 2026 Open-Source AI Challenge, Week 2&lt;/strong&gt; (theme: &lt;em&gt;Side Hustle&lt;/em&gt;).&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The problem&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Small sellers spend more time writing listings than making products. A good listing needs a title that fits the platform's limit, a clear description, key details and a full set of tags, repeated for every item. Paid AI tools charge per listing or per month, and they upload your photos and product ideas to someone else's server.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What ListingLab does&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Listing writer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Title, two-paragraph description, key details and tags from up to 4 photos and your notes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Etsy and eBay rules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Etsy: title up to 140 characters, up to 13 tags of at most 20 characters. eBay: title&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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/Sahan20030814/listinglab" 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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Gemma 3 4B (open weights), run locally with Ollama. The server calls Ollama's local chat API and asks for JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server:&lt;/strong&gt; one Python standard-library file, no framework and no pip packages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface:&lt;/strong&gt; one HTML file with inline CSS and JavaScript. Photos are shrunk to 768 px in the browser before they are sent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy:&lt;/strong&gt; a Dockerfile and a &lt;code&gt;render.yaml&lt;/code&gt; blueprint for Render. The public demo sets &lt;code&gt;LL_NO_HISTORY=1&lt;/code&gt; so visitors never see each other's listings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model writes the words. Everything that has to be right is ordinary code. The marketplace rules live in a small table, and the server clips the title to the limit and cleans the tags (lowercase, no duplicates, at most 20 characters each):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;RULES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;etsy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Etsy: title up to 140 characters, up to 13 tags, each tag at most 20 characters.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
         &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ebay&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eBay: title up to 80 characters, factual and keyword-first, up to 8 keyword tags.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The score and the profit maths are plain JavaScript, so they never depend on the model. If the model cannot read photos, the app falls back to your notes and says so. If Ollama is off, it uses a plain template and says so. It never pretends.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I found when I tested it
&lt;/h3&gt;

&lt;p&gt;I ran it on my own laptop with &lt;code&gt;gemma3:4b&lt;/code&gt;. One run with four photos and a long set of notes took 18.7 seconds and 709 tokens, and the app showed &lt;code&gt;$0.00 in API fees&lt;/code&gt;. Two honest things from that run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The model did not read my photos.&lt;/strong&gt; The copy of the model on my laptop could not take image input in that run, so ListingLab fell back to my notes and said so on screen ("Used your notes only"). Photo reading needs a vision-capable model. I have not verified photo reading end to end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model added a detail I did not give it.&lt;/strong&gt; The title it wrote included a condition, "Excellent Condition", that was not in my notes. My prompt tells it not to invent facts, and a small model still does. That is why the Check tab exists and why the app is a drafting tool, not an autopilot: read every listing before you publish it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Check tab scored that listing 50 out of 100 and flagged a short description and too few key details. For the Profit tab, an item cost of 2000, a selling price of 3000, shipping of 50 and fees of 5% gave a profit of 800.00, a 27% margin and a break-even price of 2157.89. The numbers are currency-neutral, and you enter your platform's current fee percentage because fees change.&lt;/p&gt;

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

&lt;p&gt;A listing writer sees exactly what a seller is about to sell: unreleased products, handmade designs, photos of stock at home. With open weights, that all stays on the seller's machine. The app also works with the Wi-Fi off, because the model, the server and the page are all local.&lt;/p&gt;

&lt;p&gt;Cost is the other reason. A seller with hundreds of products would pay per listing, or per month, to a closed API. Running the model locally means the marginal cost of one more listing is electricity, and the seller is not tied to someone else's pricing. The model is also one setting (&lt;code&gt;LL_MODEL&lt;/code&gt;), so it can be swapped for a different open model, and in future fine-tuned on a shop's own best listings. I have not done that fine-tuning yet.&lt;/p&gt;

&lt;p&gt;There is a trade-off, and I want to be straight about it. A 4B model writes less polished text than the biggest closed models and sometimes invents details, as my test showed. I leaned on that by keeping the limits, the score and the profit maths in code, and by labelling every fallback on screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;I built this with Claude as a coding assistant and AI-assisted drafting. I ran the app, tested it, took the screenshots and decided what to keep.&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; Gemma 3 4B runs locally through Ollama and writes every listing the app produces on my machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render:&lt;/strong&gt; the web app is deployed on Render from a Docker blueprint in the repo. The model does not run on Render, so the hosted demo uses the template fallback.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Touch Grass Planner: a local Gemma model that plans your walk, then turns the screen dark</title>
      <dc:creator>Sahan Dilshan</dc:creator>
      <pubDate>Sun, 11 Oct 2026 08:34:46 +0000</pubDate>
      <link>https://dev.to/sahan20030814/touch-grass-planner-a-local-gemma-model-that-plans-your-walk-then-turns-the-screen-dark-5f86</link>
      <guid>https://dev.to/sahan20030814/touch-grass-planner-a-local-gemma-model-that-plans-your-walk-then-turns-the-screen-dark-5f86</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;Touch Grass Planner is a web app whose whole job is to get closed quickly.&lt;/p&gt;

&lt;p&gt;You say what you feel like doing (walk, hike, run, birdwatching, gardening) and how many minutes you have. A Gemma model running on your own machine writes a short plan. The app draws a loop sized to your time, and gives you five things to notice on the way. Then you press &lt;strong&gt;Start&lt;/strong&gt; and the page turns into a dark countdown with your route on it. You leave. When you get back you press &lt;strong&gt;I'm back&lt;/strong&gt;, and it keeps your streak.&lt;/p&gt;

&lt;p&gt;It is for anyone who opens the laptop "for a minute" and would rather be outside. I built it in Sri Lanka, so it also has a garden planner with a tropical mode (Maha and Yala seasons) and a frost mode for colder places.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plan:&lt;/strong&gt; a short, specific outing plan from Gemma, using your activity, time, weather and garden jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loop:&lt;/strong&gt; a triangle route sized to your time, computed on the device from compass bearings, drawn on an OpenStreetMap map, with a walking-directions link.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What to notice:&lt;/strong&gt; a five-item checklist tied to your activity. The count goes into your journal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start timer:&lt;/strong&gt; a dark full-screen countdown that shows the loop map and legs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Garden planner:&lt;/strong&gt; what to sow this week, from a plain data table you can edit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weather:&lt;/strong&gt; live conditions from Open-Meteo when you have internet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Journal and streak:&lt;/strong&gt; stored in a local file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bird ID (optional):&lt;/strong&gt; BirdNET identifies bird sounds you record. It needs a separate install.&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%2Freasgqb2vockpbwdyhw3.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%2Freasgqb2vockpbwdyhw3.png" alt="Touch Grass Planner in a browser: a Gemma-written 30-minute walk plan, a five-item " width="800" height="1296"&gt;&lt;/a&gt;&lt;em&gt;Planner page: live weather, a plan from Gemma 3 4B running locally, a notice checklist, and a triangle loop on the map.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Live: &lt;a href="https://touch-grass-planner-oi06.onrender.com/" rel="noopener noreferrer"&gt;https://touch-grass-planner-oi06.onrender.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One honest note: the hosted copy runs on Render's free tier, which cannot run a language model. There, the app shows a clearly labelled template plan instead of Gemma. Everything else (garden, loop, map, timer, notice list) works. The first load can take up to a minute while the free instance wakes up. The real experience is the local version, where Gemma writes the plan.&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%2Fesdn7ehihu56ag7m7pj5.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%2Fesdn7ehihu56ag7m7pj5.png" alt="The dark Start screen with the countdown and the loop map" width="800" height="636"&gt;&lt;/a&gt;&lt;em&gt;The dark Start screen: a countdown with the loop map and legs. Press "I'm back" to log the outing.&lt;/em&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/Sahan20030814" rel="noopener noreferrer"&gt;
        Sahan20030814
      &lt;/a&gt; / &lt;a href="https://github.com/Sahan20030814/touch-grass-planner" rel="noopener noreferrer"&gt;
        touch-grass-planner
      &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 Planner&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A local-first outdoor companion. An open-weight model on your own machine plans the outing, then the screen goes dark and you go outside.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/Sahan20030814/touch-grass-planner/docs/cover.png"&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%2FSahan20030814%2Ftouch-grass-planner%2FHEAD%2Fdocs%2Fcover.png" alt="Touch Grass Planner: a local Gemma model plans your walk, then the screen goes dark"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Built for the &lt;strong&gt;Hacktoberfest 2026 Open-Source AI Challenge, Week 1&lt;/strong&gt; (theme: &lt;em&gt;Touch Grass&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://touch-grass-planner-oi06.onrender.com/" rel="nofollow noopener noreferrer"&gt;https://touch-grass-planner-oi06.onrender.com/&lt;/a&gt; (the hosted copy cannot run the model, so it shows a clearly labelled template plan; the first load can take up to a minute on Render's free tier)&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;th&gt;Needs internet?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Outing plan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A short, specific plan written by Gemma (via Ollama) from your activity, time, conditions, garden jobs, route, and your climate and season&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loop route&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A triangle loop sized to your time, computed on-device from compass bearings, drawn as a sketch and on a map, with a Google Maps walking link. With internet it checks OpenStreetMap tiles and rotates or shortens the loop to keep it out of&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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/Sahan20030814/touch-grass-planner" 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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Gemma 3 4B (open weights), run locally with Ollama. The server calls Ollama's local HTTP API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server:&lt;/strong&gt; one standard-library Python file, no framework and no pip installs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI:&lt;/strong&gt; one HTML file with inline CSS and JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weather and map:&lt;/strong&gt; Open-Meteo (no API key), Leaflet and OpenStreetMap tiles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bird ID:&lt;/strong&gt; BirdNET-Analyzer, optional, run on the same machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy:&lt;/strong&gt; a Dockerfile and a &lt;code&gt;render.yaml&lt;/code&gt; blueprint for Render.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main design decision was to let the model write words and nothing else. A 4B model should not be trusted with facts, so everything that has to be right is ordinary code: the crop calendars, the loop geometry and the journal. The model only gets a short, factual prompt: your activity, minutes, weather, garden jobs, the loop, and the climate and season (for example "Tropical Sri Lanka, Maha season, no autumn or winter"). If the model is not running, the app says so on screen and uses a plain template. It never pretends.&lt;/p&gt;

&lt;p&gt;One extra feature came from a problem I hit. The loop is a straight-line triangle, and on my first test it ran into the sea, because I live near the coast. So the app now reads the colour of the OpenStreetMap tile under points along the loop. If it sees water, it rotates the loop, or shortens it, until it stays on land. This needs internet, and it only avoids water, not fences or private land.&lt;/p&gt;

&lt;p&gt;To run it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ollama pull gemma3:4b
python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt;. Set &lt;code&gt;TG_MODEL&lt;/code&gt; to use a different model.&lt;/p&gt;

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

&lt;p&gt;The app is about leaving the laptop, so it should work where you go, and that includes places with no signal. With open weights the planning step runs on my machine. The plan, garden list, timer, journal and notice list all work with the wifi off. A closed API cannot promise that.&lt;/p&gt;

&lt;p&gt;It also keeps my data with me. Where I walk, when, and what I noticed is saved in a local file, not on a server I do not control.&lt;/p&gt;

&lt;p&gt;The weights are ordinary files. When the model download kept failing on my connection, there were other ways to get it instead of depending on one service. I can swap the model with one setting (&lt;code&gt;TG_MODEL&lt;/code&gt;), and the app costs nothing to run because there are no API keys or per-call fees.&lt;/p&gt;

&lt;p&gt;There is a trade-off. A 4B model is less polished than a big closed one. My first plan for a walk in Sri Lanka was called an "autumn walk", because my prompt only told it the month. I fixed that by passing the climate and season in the prompt and stripping season words that do not apply. That is why the facts live in code and the model only writes the words.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking It Outside
&lt;/h2&gt;

&lt;p&gt;I have tested this on my laptop, not on a long walk. Here is what I found. The timer, journal, notice checklist and loop all work end to end. My first loop ran into the sea, because a laptop's location is a guess from the internet connection, not GPS. That is why the app now checks the map for water and lets you click the map to set your start. Gemma also called a Sri Lanka walk an "autumn walk" at first, which is why the prompt now carries the climate and season.&lt;/p&gt;

&lt;p&gt;I did not field-test the bird identification feature on a trail. Next, I want to take it on a real walk and see how the plan and the loop hold up.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;I built this with Claude as a coding assistant, and I tested and ran it myself.&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; Gemma 3 4B runs locally through Ollama and writes every plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render:&lt;/strong&gt; the web app is deployed on Render from a Docker blueprint in the repo. The model itself runs locally, and the hosted demo uses the template fallback.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Known limits: the loop is a rough straight-line shape (use the maps link for real streets), a laptop's location can be kilometres off (you can click the map to set your start), and a small model sometimes gets details wrong.&lt;/p&gt;

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