<?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: Sasmitha Ashinsana</title>
    <description>The latest articles on DEV Community by Sasmitha Ashinsana (@sasmeee).</description>
    <link>https://dev.to/sasmeee</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%2F2882032%2F42828ca9-e9f3-40d7-956b-bed257bcee90.png</url>
      <title>DEV Community: Sasmitha Ashinsana</title>
      <link>https://dev.to/sasmeee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sasmeee"/>
    <language>en</language>
    <item>
      <title>Salli: my bank SMS became a budget while I did nothing 🦥</title>
      <dc:creator>Sasmitha Ashinsana</dc:creator>
      <pubDate>Sun, 25 Oct 2026 18:15:56 +0000</pubDate>
      <link>https://dev.to/sasmeee/salli-my-bank-sms-became-a-budget-while-i-did-nothing-491h</link>
      <guid>https://dev.to/sasmeee/salli-my-bank-sms-became-a-budget-while-i-did-nothing-491h</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;In Sri Lanka, every card swipe, bill payment, salary credit and wallet top-up already sends you an SMS. My phone has years of them. It's a complete record of where my money went and I've never once read it.&lt;/p&gt;

&lt;p&gt;Every budgeting app I tried wanted one of two things: type every expense in by hand or hand my bank login to a cloud service. I'm too lazy for the first and too paranoid for the second.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Salli&lt;/strong&gt; (සල්ලි, Sinhala for &lt;em&gt;money&lt;/em&gt;). It reads the money SMS already on my Android phone and turns them into a spending dashboard on my laptop. A local Gemma model does the reading. There's no account and no server and nothing leaves my WiFi.&lt;/p&gt;

&lt;p&gt;The whole effort on your side:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Salli on your laptop and scan the QR code with the phone app.&lt;/li&gt;
&lt;li&gt;Tap &lt;strong&gt;Send now&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Go back to being a sloth.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What you get back:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spent, income and what's left&lt;/strong&gt; for the month, with transfers between your own accounts kept out of the totals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A daily spend chart&lt;/strong&gt; with a forecast to the end of the month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where it went:&lt;/strong&gt; categories and top shops&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recurring charges&lt;/strong&gt; found from repeat SMS, with a warning when one goes up&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A roast.&lt;/strong&gt; Gemma writes one cheeky line about your month from your real numbers. It's the feature everyone asks about first.&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%2F3dnk7f9lnlp6ftdqo94n.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%2F3dnk7f9lnlp6ftdqo94n.png" alt="Salli dashboard with the Gemma roast" width="800" height="450"&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%2F55la1f14fgb763s0gwj2.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%2F55la1f14fgb763s0gwj2.png" alt="Daily spend, categories and recurring charges" width="800" height="450"&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%2F5x2nj1uhwde3sqo0eff7.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%2F5x2nj1uhwde3sqo0eff7.png" alt="Transactions with categories you can fix once" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The phone app does one job: pair, then send.&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%2Fubc0zjyv30xbrm2biod8.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%2Fubc0zjyv30xbrm2biod8.png" alt="Phone: pair with your laptop" width="800" height="1587"&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%2Fiphtqzlvrhr5i790w7ep.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%2Fiphtqzlvrhr5i790w7ep.png" alt="Phone: send money SMS" width="800" height="1587"&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%2Fais0n3r8pqrpzmoci79f.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%2Fais0n3r8pqrpzmoci79f.png" alt="Phone: sent to your laptop" width="800" height="1587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Screenshots use generated demo SMS. My real spending stays on my laptop, which is the whole point.&lt;/em&gt;&lt;/p&gt;

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

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

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sasmeee" rel="noopener noreferrer"&gt;
        sasmeee
      &lt;/a&gt; / &lt;a href="https://github.com/sasmeee/salli" rel="noopener noreferrer"&gt;
        salli
      &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;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/sasmeee/salli/brand/salli-logo-transparent.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%2Fsasmeee%2Fsalli%2FHEAD%2Fbrand%2Fsalli-logo-transparent.png" alt="Salli, a sleepy sloth hugging a coin" width="140"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Salli&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;b&gt;Your bank SMS become a budget. You do nothing.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;
  Salli reads the money SMS on your Android phone and turns them into a spending dashboard on your laptop
  using Gemma running locally. No account, no cloud, nothing leaves your WiFi
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://youtu.be/HijsZHjPO18" rel="nofollow noopener noreferrer"&gt;Demo video&lt;/a&gt; ·
  &lt;a href="https://dev.to/sasmeee/salli-my-bank-sms-became-a-budget-while-i-did-nothing-491h" rel="nofollow"&gt;DEV post&lt;/a&gt; ·
  &lt;a href="https://github.com/sasmeee/salli/releases/latest" rel="noopener noreferrer"&gt;Download&lt;/a&gt;
&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Salli&lt;/em&gt; (සල්ලි) means &lt;em&gt;money&lt;/em&gt; in Sinhala. The mascot is a sloth because the whole point is that you stay lazy.&lt;/p&gt;

&lt;p&gt;In Sri Lanka every card swipe, bill payment and salary credit already sends you an SMS. Budgeting apps want you to type all of that in again or hand your bank login to a cloud service. Salli just reads the SMS you already have.&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/sasmeee/salli/docs/screens/desktop/dashboard.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%2Fsasmeee%2Fsalli%2FHEAD%2Fdocs%2Fscreens%2Fdesktop%2Fdashboard.png" alt="Salli dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;Android phone                          Your laptop (same WiFi)
┌──────────────────┐   QR pair    ┌──────────────────────────────────────┐
│ Salli app        │ ───────────▶ │ Salli desktop (Tauri + Rust)         │
│ reads SMS inbox  │              │                                      │
│ keeps bank SMS,  │  local&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sasmeee/salli" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Windows installer and Android APK are on the &lt;a href="https://github.com/sasmeee/salli/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;v1.0.0 release&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Salli is two apps that talk only to each other:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Android phone                          Your laptop (same WiFi)
┌──────────────────┐   QR pair    ┌──────────────────────────────────────┐
│ Salli app        │ ───────────▶ │ Salli desktop (Tauri + Rust)         │
│ reads SMS inbox  │              │                                      │
│ keeps bank SMS,  │  local HTTP  │  1. rules     regex for amounts      │
│ drops OTPs       │ ───────────▶ │  2. memory    shops it already knows │
└──────────────────┘              │  3. Gemma 3   only for new shops     │
                                  │                                      │
                                  │  dashboard · roast · forecast        │
                                  └──────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The phone&lt;/strong&gt; is an Expo (React Native) app with a small Kotlin module that reads the SMS inbox. It keeps messages that look like money and drops OTPs on the phone, so they never travel anywhere. Then it posts the rest to the laptop over local HTTP.&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%2Flhg6zh4dexo86hiol9bl.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%2Flhg6zh4dexo86hiol9bl.png" alt="Pairing with a QR code and a rotating 6-digit code" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The laptop&lt;/strong&gt; is a Tauri 2 app. A Rust server (axum) listens on port 7777. The UI is React with Tailwind and shadcn/ui.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gemma does the reading, but only when it has to
&lt;/h3&gt;

&lt;p&gt;I could have sent every SMS to Gemma. On a laptop CPU that would take forever and the slow part of this challenge is supposed to be me, not the app. So reading happens in three layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rules.&lt;/strong&gt; Regex pulls out the amount and whether it's a debit or a credit. Known formats from Sri Lankan banks get fully parsed here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory.&lt;/strong&gt; Once Salli knows a shop, it remembers the shop's name and category. If you fix a wrong category, it remembers that too, for every future payment there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 3 4B&lt;/strong&gt; through Ollama, only for SMS the first two layers can't place. One SMS per call, with a strict JSON schema, so the output is always &lt;code&gt;{amount, kind, merchant, category}&lt;/code&gt; and never a paragraph.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first sync of a big inbox is slow, because Gemma meets every shop for the first time. After that, almost everything comes from rules and memory and new SMS show up instantly.&lt;/p&gt;

&lt;p&gt;Gemma also writes the roast. It gets a summary of the month (total, top categories, top shops with counts) and a sloth persona prompt with a high temperature. That gives you lines like &lt;em&gt;"Machan, seriously, six times ordering KFC?"&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  TabPFN for the forecast
&lt;/h3&gt;

&lt;p&gt;The "where your month is heading" number started as a simple average. I swapped in &lt;strong&gt;TabPFN&lt;/strong&gt;, Prior Labs' open tabular foundation model, as an optional upgrade.&lt;/p&gt;

&lt;p&gt;Each past day becomes a row: weekday, weekend or not, day of the month and days since the last salary. The target is what I spent that day. TabPFN regresses on that history and predicts the rest of the month, with no training step and no tuning. It picks up that I spend more on weekends and right after payday, which a flat average can't.&lt;/p&gt;

&lt;p&gt;It runs as a small Python sidecar that the Rust app calls. On my laptop's CPU, a forecast takes about 8 seconds. If Python or TabPFN isn't installed or there's less than 14 days of history, Salli quietly falls back to the average. Nobody has to install a 900 MB model just to try the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Things that went wrong
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One payment, three SMS.&lt;/strong&gt; A single LKR 560 phone bill sent me an alert from my bank, a receipt from the biller and a note from my wallet. That's three "payments". Salli now merges SMS with the same amount within an hour when at least one sender isn't a bank and keeps the bank's row as the main one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transfers looked like spending.&lt;/strong&gt; Moving money to my own savings made my month look terrible. Transfers are now left out of spent and income.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The QR scanner.&lt;/strong&gt; The Expo camera scanner never focused on my budget Samsung. The Google code scanner crashed on the same phone. I ended up writing a small native module around ZXing and now it scans instantly. It has a red laser line, which I've decided is a feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Play Protect&lt;/strong&gt; blocks sideloaded apps that read SMS. That's fair, honestly. The README explains how to install anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benchmark
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tools/bench.ts&lt;/code&gt; generates realistic Sri Lankan bank SMS with known answers and scores what Salli extracts. On that set, amount, direction, shop and category were all correct and every promo and OTP was skipped. Gemma 3 4B takes about 0.6 to 0.7 seconds per new SMS on CPU.&lt;/p&gt;

&lt;p&gt;Honest note: some test shops also appear as examples in the Gemma prompt, so real-world accuracy is lower. On my own inbox of about 1,400 SMS, the misses were mostly biller receipts read as income. The duplicate merge now handles those.&lt;/p&gt;

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

&lt;p&gt;Bank SMS might be the most private data on my phone. They show where I eat, what I earn, who I send money to and when I'm broke.&lt;/p&gt;

&lt;p&gt;With a closed model API, Salli would have to upload all of that to someone's server just to work out that "KEELLS SUPER" means groceries. I wouldn't use that app, so I wasn't going to build it.&lt;/p&gt;

&lt;p&gt;Open weights flip that. Gemma runs on my laptop through Ollama and TabPFN runs there too. The phone talks only to the laptop, over WiFi I own. There's no account to make, no API key to leak, no usage bill and no company that can shut the feature off or change the price later. The AI is just part of the app, like the database.&lt;/p&gt;

&lt;p&gt;Open source matters for the other half. Salli only knows Sri Lankan banks and billers today. Because the rules, the prompt and the benchmark are all in the repo, anyone can add their own bank's format and test it, without waiting for me.&lt;/p&gt;

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

&lt;p&gt;I pointed Salli at my real inbox: about 1,400 SMS going back years. I had never once added up my spending and the first time I saw it all in one place was a little uncomfortable. The roast didn't help.&lt;/p&gt;

&lt;p&gt;Before, figuring out where my money went meant scrolling through SMS and adding numbers in my head, which in practice meant never. Now it's one tap on the phone. The rest happens while I'm doing something else or nothing at all, which is the sloth way.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma:&lt;/strong&gt; Gemma 3 4B runs locally through Ollama to read new bank SMS into structured transactions with a JSON schema and to write the monthly spending roast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TabPFN:&lt;/strong&gt; TabPFN regression forecasts daily spending for the rest of the month from past days, running locally as an optional sidecar.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>ShopSnap: WhatsApp order screenshots to a daily sales report, with AI that stays on your PC</title>
      <dc:creator>Sasmitha Ashinsana</dc:creator>
      <pubDate>Sat, 17 Oct 2026 19:28:10 +0000</pubDate>
      <link>https://dev.to/sasmeee/shopsnap-whatsapp-order-screenshots-to-a-daily-sales-report-with-ai-that-stays-on-your-pc-5606</link>
      <guid>https://dev.to/sasmeee/shopsnap-whatsapp-order-screenshots-to-a-daily-sales-report-with-ai-that-stays-on-your-pc-5606</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;ShopSnap turns WhatsApp order screenshots into a daily sales report. It is made for small shops that take orders over chat.&lt;/p&gt;

&lt;p&gt;Lots of small sellers run their whole business inside WhatsApp. A customer writes "2 kg rice and 1 dozen eggs" and the owner replies with a price. By evening the orders are spread over dozens of chats. Who ordered what? Who paid? Who still owes money? Most owners answer that by scrolling back through every chat or by trusting their memory.&lt;/p&gt;

&lt;p&gt;Here is how ShopSnap handles it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Drop in the screenshots and pick the business date.&lt;/li&gt;
&lt;li&gt;A local vision model reads each chat. A local text model turns it into orders.&lt;/li&gt;
&lt;li&gt;You review and fix every order. Nothing is saved until you click Confirm.&lt;/li&gt;
&lt;li&gt;You see total sales and who has not paid and a total for each customer.&lt;/li&gt;
&lt;li&gt;Mark orders as paid (full or part) and copy a ready follow-up message to paste into WhatsApp.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chats that show only a saved name go to a Pending list. You attach a phone number once and ShopSnap remembers that name from then on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who would pay for it:&lt;/strong&gt; any shop owner who sells over chat and keeps the books by hand. The app is free and MIT licensed today. The version I would sell is a packaged desktop app with the setup done for the shop owner. That part is future work.&lt;/p&gt;

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

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

&lt;p&gt;&lt;strong&gt;Upload and review.&lt;/strong&gt; Drop screenshots then check and fix every order before saving.&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%2Fioxnj239p2dsluf2gncm.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%2Fioxnj239p2dsluf2gncm.png" alt="Upload and review screen" width="799" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pending.&lt;/strong&gt; Chats that show only a name wait here until you attach a phone number.&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%2Fsxq76gdvec247b1vct30.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%2Fsxq76gdvec247b1vct30.png" alt="Pending list" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Daily report.&lt;/strong&gt; Total sales and who has not paid and a total for each customer.&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%2F325b9w4vd96awrb4zcor.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%2F325b9w4vd96awrb4zcor.png" alt="Daily report" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All screenshots use fake demo data.&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/sasmeee" rel="noopener noreferrer"&gt;
        sasmeee
      &lt;/a&gt; / &lt;a href="https://github.com/sasmeee/shopsnap" rel="noopener noreferrer"&gt;
        shopsnap
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Turn WhatsApp order screenshots into a daily sales report. Runs fully locally with Ollama. Customer data never leaves your PC.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&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%2Fraw.githubusercontent.com%2Fsasmeee%2Fshopsnap%2Fmain%2Fpublic%2Flogo.svg" 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%2Fraw.githubusercontent.com%2Fsasmeee%2Fshopsnap%2Fmain%2Fpublic%2Flogo.svg" alt="ShopSnap logo" width="100"&gt;&lt;/a&gt;
  
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;ShopSnap&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  Turn WhatsApp order screenshots into a daily sales report. Built for small shops.&lt;br&gt;
  The AI runs on your own PC. Customer numbers and sales never leave it
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://youtu.be/XzX9DlZBfwg" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/85b9151c1558ff2e3b975a70fe9e0b6e9c96110b836312358a77b2f47f7d3432/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f44656d6f5f766964656f2d596f75547562652d633433303262" alt="Demo video"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a2b318140d58f56862ecd5581d006b961f44164e891dbd5fd6834b93f824756f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f626572666573742d323032362d626c756576696f6c6574"&gt;&lt;img src="https://camo.githubusercontent.com/a2b318140d58f56862ecd5581d006b961f44164e891dbd5fd6834b93f824756f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f626572666573742d323032362d626c756576696f6c6574" alt="Hacktoberfest 2026"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/84db1291cc1c8b103b419b762216c0c4e26d086c9d3cc09a050b85d0edcc99c0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41492d72756e735f6c6f63616c6c795f776974685f4f6c6c616d612d303437383537"&gt;&lt;img src="https://camo.githubusercontent.com/84db1291cc1c8b103b419b762216c0c4e26d086c9d3cc09a050b85d0edcc99c0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41492d72756e735f6c6f63616c6c795f776974685f4f6c6c616d612d303437383537" alt="Local AI with Ollama"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b431578169ca0eb5258a07c4e015e7c912617c2d38a7c6b3b73b0c42901da540/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f507269766163792d73746179735f6f6e5f796f75725f50432d303437383537"&gt;&lt;img src="https://camo.githubusercontent.com/b431578169ca0eb5258a07c4e015e7c912617c2d38a7c6b3b73b0c42901da540/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f507269766163792d73746179735f6f6e5f796f75725f50432d303437383537" alt="Data stays on your PC"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/sasmeee/shopsnap/releases" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/de419642bf191b480233932735667d8b157e74dc3d52f60a8376a00fe1cad7c0/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f7361736d6565652f73686f70736e61703f636f6c6f723d303437383537" alt="Latest release"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5caa455d8debc46fb23abbadb45a733a937f3910a73fc875c2f7820468e1bb54/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e"&gt;&lt;img src="https://camo.githubusercontent.com/5caa455d8debc46fb23abbadb45a733a937f3910a73fc875c2f7820468e1bb54/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e" alt="MIT"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12" rel="nofollow"&gt;Hacktoberfest Open-Source AI Challenge, Week 2: Side Hustle&lt;/a&gt;.&lt;br&gt;
The repository was started on October 15, 2026, inside the challenge window.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Demo video:&lt;/strong&gt; &lt;a href="https://youtu.be/XzX9DlZBfwg" rel="nofollow noopener noreferrer"&gt;Watch on YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DEV post:&lt;/strong&gt; &lt;a href="https://dev.to/sasmeee/shopsnap-whatsapp-order-screenshots-to-a-daily-sales-report-with-ai-that-stays-on-your-pc-5606" rel="nofollow"&gt;ShopSnap: WhatsApp order screenshots to a daily sales report, with AI that stays on your PC&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Upload and review.&lt;/strong&gt; Drop screenshots, then check and fix every order before saving.&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/sasmeee/shopsnap/docs/upload-review.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%2Fsasmeee%2Fshopsnap%2FHEAD%2Fdocs%2Fupload-review.png" alt="Upload and review screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pending.&lt;/strong&gt; Chats that show only a name wait here until you attach a phone number.&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/sasmeee/shopsnap/docs/pending.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%2Fsasmeee%2Fshopsnap%2FHEAD%2Fdocs%2Fpending.png" alt="Pending list"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Daily report.&lt;/strong&gt; Total sales, who has not paid, and per-customer totals.&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/sasmeee/shopsnap/docs/report.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%2Fsasmeee%2Fshopsnap%2FHEAD%2Fdocs%2Freport.png" alt="Daily report"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All screenshots use fake demo data.&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;ol&gt;

&lt;li&gt;Drop WhatsApp screenshots and pick the business date.&lt;/li&gt;

&lt;li&gt;A local vision model reads the chat. A local text model turns it into orders.&lt;/li&gt;

&lt;li&gt;You review and…&lt;/li&gt;

&lt;/ol&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sasmeee/shopsnap" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


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

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; Next.js (App Router and TypeScript) with shadcn/ui and Tailwind. SQLite through better-sqlite3. Ollama runs the models. zod checks the JSON. libphonenumber-js handles phone numbers and sharp resizes the images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open models, both run locally through Ollama:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gemma3:4b&lt;/code&gt; reads the screenshot and writes out the chat as text.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;qwen2.5:3b&lt;/code&gt; takes that text and extracts the orders as JSON.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The pipeline:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screenshot -&amp;gt; resize -&amp;gt; vision model (text) -&amp;gt; text model (JSON)
-&amp;gt; code checks -&amp;gt; you review -&amp;gt; SQLite -&amp;gt; report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two small models on a normal PC will make mistakes. So I did not build the app around trusting them. I built it around checking them. The model proposes and plain code decides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A phone number is the only customer ID. A chat name never creates a customer.&lt;/li&gt;
&lt;li&gt;Code decides whether the chat header is a number or a name. The model does not.&lt;/li&gt;
&lt;li&gt;Every amount the model returns must appear in the chat text. Items must add up to the total. A failed check blocks the Confirm button.&lt;/li&gt;
&lt;li&gt;"Paid" is accepted only if the chat has payment words. Otherwise the status becomes unknown and the owner decides.&lt;/li&gt;
&lt;li&gt;The same screenshot twice is never counted twice. I hash it and store the order position.&lt;/li&gt;
&lt;li&gt;Only the hash is stored. The image is never kept.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tested the pipeline on 10 fake screenshots with &lt;code&gt;bun run eval&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Screenshots&lt;/td&gt;
&lt;td&gt;10 (fake chats)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orders&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fields correct&lt;/td&gt;
&lt;td&gt;90 of 107 (84.1%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screenshots with every field right&lt;/td&gt;
&lt;td&gt;8 of 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orders with a wrong field&lt;/td&gt;
&lt;td&gt;3 of 11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong orders the code checks did not flag&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average time per screenshot&lt;/td&gt;
&lt;td&gt;about 26 s on my PC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One screenshot did most of the damage. Chat 07 holds two orders. The model returned one and misread its amounts. That single chat caused 15 of the 17 wrong fields. The other miss was in chat 09 where the model misspelled a name and guessed "unpaid" when the status should have been "unknown".&lt;/p&gt;

&lt;p&gt;Two wrong orders got past the code checks. One of them is the order the model never returned. Nothing can validate an order that is not there.&lt;/p&gt;

&lt;p&gt;What I learned: a 4B model is weakest on chats with more than one order. The checks are good at catching wrong numbers but they cannot catch a missing order. A forced re-read can find an order the first read missed. The review screen is the last safety net before anything is saved.&lt;/p&gt;

&lt;p&gt;Real chats are messier than my fake ones so expect lower numbers. That is why the review screen is not optional.&lt;/p&gt;

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

&lt;p&gt;A shop's chats hold customer phone numbers and daily sales. That is private data. With a closed API every screenshot would leave the shop's PC and travel to someone else's server. Many owners would never accept that and I would not blame them.&lt;/p&gt;

&lt;p&gt;Open weights changed three things for ShopSnap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy:&lt;/strong&gt; the models run on the owner's own machine. Numbers and sales never leave it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; there is no bill for each screenshot. A shop that processes 40 chats a day pays nothing extra.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control:&lt;/strong&gt; the models are two environment variables. If a better small model comes out, you change &lt;code&gt;VISION_MODEL&lt;/code&gt; or &lt;code&gt;TEXT_MODEL&lt;/code&gt; and you are done. It also works offline after the first download.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The honest trade is accuracy. Small local models are weaker than the biggest hosted ones. I answered that with code checks and a human review step instead of a bigger model.&lt;/p&gt;

&lt;p&gt;Both models keep their own licenses. Gemma 3 is under the Gemma Terms of Use and Qwen2.5 3B is under the Qwen Research License. The README tells anyone to read both before using ShopSnap in a paid setting.&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 (&lt;code&gt;gemma3:4b&lt;/code&gt;) is the vision model that reads every screenshot. Without it nothing else in the pipeline has any text to work on.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
    </item>
    <item>
      <title>SafeCamp: an offline campsite safety check that runs on your phone</title>
      <dc:creator>Sasmitha Ashinsana</dc:creator>
      <pubDate>Sun, 11 Oct 2026 10:40:54 +0000</pubDate>
      <link>https://dev.to/sasmeee/safecamp-an-offline-campsite-safety-check-that-runs-on-your-phone-1af</link>
      <guid>https://dev.to/sasmeee/safecamp-an-offline-campsite-safety-check-that-runs-on-your-phone-1af</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;p&gt;You find the perfect spot. Flat ground, a stream nearby, no one around. You also have zero bars and there is a grey papery lump hanging from the branch above where you wanted to put your tent.&lt;/p&gt;

&lt;p&gt;That moment is why I built SafeCamp.&lt;/p&gt;

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

&lt;p&gt;SafeCamp is a campsite safety inspector that works with no signal. Point your phone at a plant, an insect, a nest or a patch of ground. It tells you how worried to be and what to do next, in four levels: &lt;strong&gt;Favorable&lt;/strong&gt;, &lt;strong&gt;Caution&lt;/strong&gt;, &lt;strong&gt;Danger&lt;/strong&gt;, &lt;strong&gt;Critical&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example advice: “Move your tent pitch at least 30 m away.”&lt;/p&gt;

&lt;p&gt;The whole check runs on the phone. You open the app once on Wi-Fi, the vision model is cached and from then on it works in airplane mode. The photo never leaves the device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it fits “Touch Grass”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Most apps compete for your attention. This one is built to give it back. It is only useful when you are standing outside, in the place you are about to sleep and it is meant to take seconds: scan, read the card, move the tent, put the phone away.&lt;/p&gt;

&lt;p&gt;It is for anyone who camps, hikes or picnics somewhere unfamiliar. It removes one reason to stay home: “what if I run into something I cannot identify and cannot look up?”&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

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

&lt;p&gt;Live app: &lt;a href="https://safecamp.onrender.com" rel="noopener noreferrer"&gt;safecamp.onrender.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
(Open it once on Wi-Fi, then try airplane mode.)&lt;/p&gt;
&lt;h3&gt;
  
  
  Four real scans from the app
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
&lt;tr&gt;
    &lt;td&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4nzt76e8er0na7pz862j.webp" alt="Snake result" width="800" height="1422"&gt;
      &lt;br&gt;&lt;strong&gt;RISK: DANGER&lt;/strong&gt;&lt;br&gt;Snake - 83%
    &lt;/td&gt;
    &lt;td&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7dykn56sipbrmadcefbx.webp" alt="Wasp nest result" width="800" height="1422"&gt;
      &lt;br&gt;&lt;strong&gt;RISK: DANGER&lt;/strong&gt;&lt;br&gt;Wasp or hornet nest - 95%
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuee9sbhltl1d1cnpxfae.webp" alt="Pet dog result" width="800" height="1422"&gt;
      &lt;br&gt;&lt;strong&gt;NO KNOWN HAZARD&lt;/strong&gt;&lt;br&gt;Looks like: Pet dog - 97%
    &lt;/td&gt;
    &lt;td&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4t7dmk1s3sq1kis11a1n.webp" alt="Tent campsite result" width="800" height="1422"&gt;
      &lt;br&gt;&lt;strong&gt;NOT SURE, BE CAREFUL&lt;/strong&gt;&lt;br&gt;Possible: Risky campfire - 38%
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sasmeee" rel="noopener noreferrer"&gt;
        sasmeee
      &lt;/a&gt; / &lt;a href="https://github.com/sasmeee/safe-camp" rel="noopener noreferrer"&gt;
        safe-camp
      &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;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/sasmeee/safe-camp/main/public/safecamp-logo-light.svg"&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%2Fsasmeee%2Fsafe-camp%2Fmain%2Fpublic%2Fsafecamp-logo-light.svg" alt="SafeCamp logo" width="120"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;SafeCamp&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  An offline campsite safety inspector. Take a photo of a plant, insect or piece of ground,&lt;br&gt;
  and get a color-coded risk level and what to do about it, with no signal and no upload
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://safecamp.onrender.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/66513384fa00e5e4cddfaf2e6da99a96d0162510d619fec7413fb5793b616873/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6976655f64656d6f2d7361666563616d702e6f6e72656e6465722e636f6d2d316233383262" alt="Live demo"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a2b318140d58f56862ecd5581d006b961f44164e891dbd5fd6834b93f824756f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f626572666573742d323032362d626c756576696f6c6574"&gt;&lt;img src="https://camo.githubusercontent.com/a2b318140d58f56862ecd5581d006b961f44164e891dbd5fd6834b93f824756f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f626572666573742d323032362d626c756576696f6c6574" alt="Hacktoberfest 2026"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0a68507d8e6e74effd7d97aaea03bcbdfd32168d60bc2f8e29a493e74d64c7e7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5057412d6f66666c696e655f66697273742d326637643466"&gt;&lt;img src="https://camo.githubusercontent.com/0a68507d8e6e74effd7d97aaea03bcbdfd32168d60bc2f8e29a493e74d64c7e7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5057412d6f66666c696e655f66697273742d326637643466" alt="Offline first"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/db64d3df3fad18238b0c92642b83b47a207d406bf1b29775139f97c20163fe71/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41492d6f6e2d2d6465766963652d326637643466"&gt;&lt;img src="https://camo.githubusercontent.com/db64d3df3fad18238b0c92642b83b47a207d406bf1b29775139f97c20163fe71/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41492d6f6e2d2d6465766963652d326637643466" alt="On-device AI"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5caa455d8debc46fb23abbadb45a733a937f3910a73fc875c2f7820468e1bb54/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e"&gt;&lt;img src="https://camo.githubusercontent.com/5caa455d8debc46fb23abbadb45a733a937f3910a73fc875c2f7820468e1bb54/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e" alt="MIT"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05" rel="nofollow"&gt;Hacktoberfest Open-Source AI Challenge, Week 1: Touch Grass&lt;/a&gt;.&lt;br&gt;
The repository was started on October 8, 2026, inside the challenge window.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://safecamp.onrender.com" rel="nofollow noopener noreferrer"&gt;safecamp.onrender.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo video:&lt;/strong&gt; &lt;a href="https://youtu.be/T_eaoRa1YOM" rel="nofollow noopener noreferrer"&gt;Watch on YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DEV post:&lt;/strong&gt; &lt;a href="https://dev.to/sasmeee/safecamp-an-offline-campsite-safety-check-that-runs-on-your-phone-1af" rel="nofollow"&gt;SafeCamp: an offline campsite safety check that runs on your phone&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why this exists&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Good camping spots tend to have bad phone signal. That is exactly where you meet an unfamiliar insect, a plant you do not want to touch, or a dry gully you should not sleep in. Searching the web is not an option there, and a photo of your campsite is not something everyone wants to upload anyway.&lt;/p&gt;

&lt;p&gt;SafeCamp runs the whole check on the phone. The photo…&lt;/p&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sasmeee/safe-camp" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;MIT licensed. The README covers running it locally and adding your own hazards.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  The model
&lt;/h3&gt;

&lt;p&gt;SafeCamp uses CLIP (&lt;code&gt;Xenova/clip-vit-base-patch32&lt;/code&gt;, int8 ONNX) through Transformers.js. It runs on WebAssembly inside a Web Worker so the UI never freezes.&lt;/p&gt;

&lt;p&gt;There is no training. CLIP compares the photo with text prompts like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“a photo of a paper wasp nest with hexagonal honeycomb cells”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The closest prompts win. Adding a hazard means writing a few sentences, not collecting a dataset.&lt;/p&gt;

&lt;h3&gt;
  
  
  The hazard list
&lt;/h3&gt;

&lt;p&gt;41 entries live in one TypeScript file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;37 hazards (poisonous plants, stinging insects, snakes and large animals, unsafe terrain and water, fire and weather risks)&lt;/li&gt;
&lt;li&gt;4 favorable signs (e.g. a flat, dry tent site)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each entry has prompts, a short summary and action steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making it fast
&lt;/h3&gt;

&lt;p&gt;The first version re-encoded every text prompt on every scan.&lt;br&gt;&lt;br&gt;
With 5 hazards it was fine (~1.4 s). At 41 entries it grew to 3.6–5 s on my laptop.&lt;/p&gt;

&lt;p&gt;The text side never changes, so I encode it once at dev time into &lt;code&gt;text-embeddings.json&lt;/code&gt;. The phone only runs the image encoder. A scan is now back under 1.5 s. A script regenerates the file whenever prompts change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making it honest
&lt;/h3&gt;

&lt;p&gt;This is where I spent the most time - a wrong “you’re fine” is worse than no answer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Early on, a photo of an empty bird nest came back as “wasp nest, 97%”. The app had no bird nest class, so the nearest hazard won. I added 25 everyday labels (grass, bark, bird nests, rocks) so ordinary things stop being forced into a hazard.&lt;/li&gt;
&lt;li&gt;A real hazard always outranks a favorable sign.&lt;/li&gt;
&lt;li&gt;Related hazards pool their scores (a wasp-or-bee split still counts as “stinging insects”).&lt;/li&gt;
&lt;li&gt;Low confidence gives a “possible, be careful” warning that names the runner-up - never reassurance.&lt;/li&gt;
&lt;li&gt;“No known hazard matched” is never shown as “safe”.&lt;/li&gt;
&lt;li&gt;Mushrooms, berries and snakes always say “treat as dangerous”, because a general vision model cannot reliably tell harmless ones from deadly ones.&lt;/li&gt;
&lt;li&gt;Dark or blurry photos are flagged instead of judged.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Why?&lt;/strong&gt; panel shows the top scores. The score is a relative share between the app’s own labels, not a probability and the app says so.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Working offline
&lt;/h3&gt;

&lt;p&gt;The app is a PWA cached by Serwist. One thing that bit me: by default the ONNX runtime loads its WebAssembly files from a CDN, which fails with no signal. I copy them into &lt;code&gt;public/ort&lt;/code&gt; at build time and serve them myself.&lt;/p&gt;

&lt;h3&gt;
  
  
  On phones
&lt;/h3&gt;

&lt;p&gt;Taking a photo on some phones reloaded the page and lost the result. I added an in-app camera and a gallery button on the result screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stack
&lt;/h3&gt;

&lt;p&gt;Next.js 16, React 19, Bun, Tailwind CSS 4, Serwist, Transformers.js, TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI assistance
&lt;/h3&gt;

&lt;p&gt;I built SafeCamp with Claude as a pair programmer for design discussion and code review. I ran, tested and debugged everything myself. I deliberately avoided cloud APIs (hosted LLMs, databases, text-to-speech inside the app) because they would break the offline and privacy promises.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limits
&lt;/h3&gt;

&lt;p&gt;SafeCamp is a first-pass aid, not an authority. CLIP is a general model and will be wrong sometimes, especially on blurry, dark, distant or unusual photos. It has not been tested on a wide range of phones. Hazards differ by region and the advice is general. Follow local rangers, signs and emergency services.&lt;/p&gt;

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

&lt;p&gt;SafeCamp only makes sense because the model is open.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt; - An open-weight model can run on the device, so photos of someone’s campsite and kids are never uploaded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No signal&lt;/strong&gt; - A hosted vision API cannot work in the places this app is for. A model that is just a file on the phone can.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cost per scan&lt;/strong&gt; - No API key, no usage bill, nothing to rate-limit. A free tool for campers should stay free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Changeable&lt;/strong&gt; - The hazard list is plain text. Someone in Sri Lanka, Norway or Arizona can swap in their own hazards and thresholds without asking anyone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspectable&lt;/strong&gt; - Because the scoring is ours, I could build a Why? panel that shows exactly how a result came out. A black-box API would not let me do that.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render&lt;/strong&gt; - SafeCamp is deployed on Render as a free Web Service (&lt;code&gt;bun install &amp;amp;&amp;amp; bun run build&lt;/code&gt;, then &lt;code&gt;bun run start&lt;/code&gt;) and it auto-deploys on push.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of GitHub&lt;/strong&gt; - GitHub Actions runs lint and build on every push and pull request (&lt;code&gt;.github/workflows/ci.yml&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs&lt;/strong&gt; - I used ElevenLabs to generate the narration for the demo video. The app itself does not call ElevenLabs, so the offline and privacy promises stay true.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Contact: &lt;a href="mailto:dev.sasmitha@gmail.com"&gt;dev.sasmitha@gmail.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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