<?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: Vaibhav Agrawal</title>
    <description>The latest articles on DEV Community by Vaibhav Agrawal (@vaibhav_agrawal_562).</description>
    <link>https://dev.to/vaibhav_agrawal_562</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%2F4163101%2F4c54b8e9-57d5-4fd9-a69c-64094ec907de.png</url>
      <title>DEV Community: Vaibhav Agrawal</title>
      <link>https://dev.to/vaibhav_agrawal_562</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vaibhav_agrawal_562"/>
    <language>en</language>
    <item>
      <title>Listing Writer</title>
      <dc:creator>Vaibhav Agrawal</dc:creator>
      <pubDate>Sat, 17 Oct 2026 17:41:06 +0000</pubDate>
      <link>https://dev.to/vaibhav_agrawal_562/listing-writer-59h7</link>
      <guid>https://dev.to/vaibhav_agrawal_562/listing-writer-59h7</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;&lt;strong&gt;Listing Writer&lt;/strong&gt; is a local-first, AI-powered listing generator for Etsy and eBay sellers. &lt;/p&gt;

&lt;p&gt;Imagine a vintage reseller or a handmade crafter with 20 items to photograph and list over the weekend. They don't have a copywriter. They use a generic cloud AI tool, but it forces them to upload photos of unreleased inventory to a server they don't control. Worse, generic AI often hallucinates details—confidently writing "approximately 8 inches wide" for an item it cannot actually measure.&lt;/p&gt;

&lt;p&gt;Listing Writer solves this by running entirely on the seller's laptop. It uses an open-weight vision model to read the photo, generate a title, description, tags, and price range, and streams the result instantly. &lt;/p&gt;

&lt;p&gt;But its most important feature is &lt;strong&gt;honesty&lt;/strong&gt;: if the model cannot see the size, material, or quantity in the photo, it writes &lt;code&gt;[seller to confirm]&lt;/code&gt; instead of guessing. The seller gets a perfect starting point without being fed AI hallucinations.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;(Note: Because I am running Gemma 3 locally on my CPU, text generation takes about 90 seconds. The video is trimmed to show the setup, the offline proof, and the final result.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=gXQ9E0Teu8s" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=gXQ9E0Teu8s&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

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


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://github.com/competitive-programmer04" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F198697320%3Fv%3D4%3Fs%3D400" height="460" class="m-0" width="460"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://github.com/competitive-programmer04" rel="noopener noreferrer" class="c-link"&gt;
            competitive-programmer04 (Vaibhav Agrawal) · GitHub
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            competitive-programmer04 has 27 repositories available. Follow their code on GitHub.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.githubassets.com%2Ffavicons%2Ffavicon.svg" width="32" height="32"&gt;
          github.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;The stack is incredibly lightweight: &lt;strong&gt;Python, Streamlit, and Ollama.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;I used &lt;strong&gt;Gemma 3 (4B)&lt;/strong&gt;, an open-weight vision model, served locally via &lt;strong&gt;Ollama&lt;/strong&gt;. The Streamlit frontend communicates with the local Ollama server via &lt;code&gt;localhost:11434&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the user clicks "Generate," the app reads the image bytes, packages them with a carefully engineered prompt (which includes the selected "voice"), and sends them to Ollama. The app uses &lt;code&gt;ollama.chat(..., stream=True)&lt;/code&gt; so the text renders progressively on the screen rather than making the user stare at a blank loading screen.&lt;/p&gt;

&lt;p&gt;Because I built this for real people, I also focused heavily on &lt;strong&gt;error handling&lt;/strong&gt;. Here is what happens when the user doesn't have Ollama running, and what the one-time ~3.3GB model download looks like:&lt;/p&gt;

&lt;p&gt;&lt;strong&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%2Ff3xw4wp8jj403eigyitb.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%2Ff3xw4wp8jj403eigyitb.png" alt="Ollama not running in the background" width="800" height="527"&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&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%2Fj00pze8sf9dl9rpf7hdr.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%2Fj00pze8sf9dl9rpf7hdr.png" alt="One Time model download" width="800" height="529"&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;For this specific use case, &lt;strong&gt;closed APIs are a dealbreaker.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Privacy &amp;amp; Unreleased Inventory:&lt;/strong&gt; Sellers often list handmade items or vintage finds they haven't publicly announced yet. Uploading those photos to a third-party API (like OpenAI or Anthropic) means that data is on a server they don't control. With local open weights, the photo never leaves the laptop.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero Marginal Cost:&lt;/strong&gt; A seller listing 200 items a month would be billed for 200 vision API calls. With an open model running locally, the cost of generating 1 or 1,000 listings is exactly $0. The only cost is the electricity to run the laptop.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;True Offline Capability:&lt;/strong&gt; Sellers source inventory at flea markets, warehouses, and craft fairs—places with terrible WiFi. A closed API fails completely without internet. My tool works completely offline after the initial one-time model download.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No Lock-in:&lt;/strong&gt; If Gemma 3 isn't good enough for a specific niche, I can swap the &lt;code&gt;model="gemma3:4b"&lt;/code&gt; string to &lt;code&gt;llama3.2-vision&lt;/code&gt; in a single line of code. I am not locked into one provider's pricing changes or deprecations.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Touch Grass Birder: On-Device Bird ID in the Browser with Open-Weight AI</title>
      <dc:creator>Vaibhav Agrawal</dc:creator>
      <pubDate>Sat, 10 Oct 2026 15:54:50 +0000</pubDate>
      <link>https://dev.to/vaibhav_agrawal_562/touch-grass-birder-on-device-bird-id-in-the-browser-with-open-weight-ai-355f</link>
      <guid>https://dev.to/vaibhav_agrawal_562/touch-grass-birder-on-device-bird-id-in-the-browser-with-open-weight-ai-355f</guid>
      <description>&lt;ul&gt;
&lt;li&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;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Touch Grass Birder is an offline, on-device bird call identifier that runs entirely in your browser. Record 6 seconds of a bird call, get a species ID in seconds, then put your phone away and go find the bird.&lt;/p&gt;

&lt;p&gt;It identifies 6,522 species using the BirdNET v2.4 open model, running locally on your device via ONNX Runtime WebAssembly. There is no server, no API key, and no internet required after the first load.&lt;/p&gt;

&lt;p&gt;It's for anyone who's ever heard a bird on a trail, pulled out their phone, and realized their bird ID app doesn't work — because it needs a server round trip, and there's no signal where the birds actually are.&lt;/p&gt;

&lt;p&gt;The whole design is built around minimizing screen time. The user taps "Record 6 seconds," sees the result, and is told to pocket their phone and go find the bird. The screen is the shortest part of the experience.&lt;/p&gt;

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

&lt;p&gt;Live app: &lt;a href="https://hacktoberfest-week1-challenge.vercel.app/" rel="noopener noreferrer"&gt;https://hacktoberfest-week1-challenge.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/competitive-programmer04/_hacktoberfest_week1_challenge" rel="noopener noreferrer"&gt;https://github.com/competitive-programmer04/_hacktoberfest_week1_challenge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With network — first-time setup:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ql2b567t8tuqaiq3d8s.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ql2b567t8tuqaiq3d8s.jpeg" alt="With internet connection only for the first time" width="720" height="1399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Airplane mode. No network. Still identifying birds.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3on6sthh9iedjdi4uv2p.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3on6sthh9iedjdi4uv2p.jpeg" alt="Without internet connection for each subsequent request after the very first request but the bird's photo will not download because I am fetching it from iNaturalist page" width="720" height="1399"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Repo: &lt;a href="https://github.com/competitive-programmer04/_hacktoberfest_week1_challenge" rel="noopener noreferrer"&gt;https://github.com/competitive-programmer04/_hacktoberfest_week1_challenge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Key files:&lt;/p&gt;

&lt;p&gt;frontend/src/audio.js — resampling and 3-second chunking with 1.5s overlap&lt;/p&gt;

&lt;p&gt;frontend/src/inference-worker.js — ONNX Runtime WebAssembly session, sigmoid post-processing&lt;/p&gt;

&lt;p&gt;frontend/src/model.js — model download, Cache Storage, and worker orchestration&lt;/p&gt;

&lt;p&gt;frontend/src/pages/Record.jsx — the recording UX and result display&lt;/p&gt;

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

&lt;p&gt;The entire stack is open-source. This project uses an open-weight model, an open-source inference framework, and local inference.&lt;/p&gt;

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

&lt;p&gt;BirdNET v2.4 — an open acoustic classifier from the Cornell Lab of Ornithology. 6,522 species, open weights, trained on real-world field recordings. I used the INT8 ARM variant (converted by tphakala) — ~47 MB, optimized for mobile CPUs.&lt;/p&gt;

&lt;p&gt;ONNX Runtime Web — the open-source inference engine. Runs the model via WebAssembly directly in the browser, no installation, no backend.&lt;/p&gt;

&lt;p&gt;React + Vite — the frontend.&lt;/p&gt;

&lt;p&gt;vite-plugin-pwa — makes the app installable and offline-capable.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Record 6 seconds via MediaRecorder, producing a WebM/Opus blob.

Decode and resample to 48 kHz using the Web Audio API.

Chunk into 3-second windows with 1.5-second overlap.

Send each chunk to a Web Worker running ONNX Runtime WebAssembly.

Run inference — the model outputs logits for 6,522 species.

Apply sigmoid to convert logits to confidence scores.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Sort and filter — top match plus alternatives, with a low-confidence fallback if nothing scores above 25%.&lt;/p&gt;

&lt;p&gt;The caching strategy: the service worker precaches the app shell and the ONNX Runtime WASM files. The 47 MB BirdNET model is downloaded from Hugging Face on first load and cached in the browser's Cache Storage by model.js. After first load, every subsequent visit serves everything from disk — no network required.&lt;/p&gt;

&lt;p&gt;A few things I had to learn the hard way:&lt;/p&gt;

&lt;p&gt;PWAs can't run a Python backend. My first version was FastAPI + Python BirdNET. It worked, but it required a laptop nearby. If the model doesn't run on the phone, "offline" is marketing, not engineering. Porting to ONNX Runtime Web meant rewriting the entire inference pipeline in JavaScript.&lt;/p&gt;

&lt;p&gt;The ONNX Runtime WASM file is 14 MB, not the 3 MB I assumed. Workbox's default precache limit is 2 MiB, so the build failed until I raised maximumFileSizeToCacheInBytes.&lt;/p&gt;

&lt;p&gt;BirdNET uses sigmoid, not softmax. It's a multi-label classifier — multiple species can be present simultaneously. I initially used softmax and got nonsensical confidence scores.&lt;/p&gt;

&lt;p&gt;Threaded WASM requires SharedArrayBuffer, which needs COOP/COEP headers. Single-threaded WASM is slower but works everywhere without special server config, and it's fast enough for a 3-second chunk.&lt;/p&gt;

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

&lt;p&gt;This project literally would not exist without open-source AI.&lt;/p&gt;

&lt;p&gt;Offline is only possible with open weights. A closed bird ID API requires a network round trip. The best places to hear birds — deep forest, high alpine, remote wetland — are exactly where that round trip fails. Only an open-weight model can be downloaded to the device and run locally. This isn't a nice-to-have; it's the entire premise.&lt;/p&gt;

&lt;p&gt;Privacy by architecture, not policy. A rare sighting is sensitive. It reveals where you were, when you were there, and what you saw. With a closed API, that data goes to a company server. With BirdNET running on-device, the audio never leaves your phone. Not because of a privacy policy because there is no server to send it to.&lt;/p&gt;

&lt;p&gt;Zero marginal cost. No API bills, no rate limits, no monthly fees. A free public tool is only viable when the marginal cost of one more user is zero. With open weights, it is.&lt;/p&gt;

&lt;p&gt;Portability and no lock-in. The same BirdNET model runs on Python, Node.js, a Raspberry Pi, and — as here — in the browser via ONNX Runtime. If a better runtime appears tomorrow, you swap it in without retraining. No vendor owns your pipeline.&lt;/p&gt;

&lt;p&gt;Inspectable and modifiable. Anyone can inspect the model, fine-tune it on local species, or replace it with a different classifier. A closed system gives you a black box and a rate limit. This gives you a file and a license.&lt;/p&gt;

&lt;p&gt;The challenge asked for open innovation, and the honest answer is that the open pieces aren't a bonus feature bolted onto a closed core. They are the product.&lt;/p&gt;

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