<?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: Hendra Hendriana</title>
    <description>The latest articles on DEV Community by Hendra Hendriana (@hendrahend).</description>
    <link>https://dev.to/hendrahend</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%2F1191591%2Feb911702-239b-4c1d-83aa-a220d465a248.jpeg</url>
      <title>DEV Community: Hendra Hendriana</title>
      <link>https://dev.to/hendrahend</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hendrahend"/>
    <language>en</language>
    <item>
      <title>Afterlight: the ghost you leave in the machine, sealed on your own laptop</title>
      <dc:creator>Hendra Hendriana</dc:creator>
      <pubDate>Fri, 30 Oct 2026 16:19:27 +0000</pubDate>
      <link>https://dev.to/hendrahend/afterlight-the-ghost-you-leave-in-the-machine-sealed-on-your-own-laptop-5885</link>
      <guid>https://dev.to/hendrahend/afterlight-the-ghost-you-leave-in-the-machine-sealed-on-your-own-laptop-5885</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;When someone dies, their family inherits a ghost: accounts nobody can find, subscriptions that keep billing, a laptop no one can unlock, thousands of photos on a drive no one knew existed. Almost nobody leaves a map.&lt;/p&gt;

&lt;p&gt;Afterlight is that map. It is a local-first digital estate planner. A language model running on your own laptop interviews you, one plain question at a time, and turns your answers into a letter of instruction for the people you love: where things are, who to call, what to cancel, what you want. The letter is encrypted, and the key is split into five printable cards. Any three unlock it. Nothing ever touches a server.&lt;/p&gt;

&lt;p&gt;I took "ghost in the machine" literally, and kindly. Día de los Muertos is about keeping the dead present through memory and care. Afterlight is the practical side of that: the last thing you do so that your family's grief is not also a scavenger hunt. It is not a chatbot that impersonates the dead. It never pretends to be anyone.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Interviews you.&lt;/strong&gt; A local model asks natural follow-ups, but a state machine decides which topics are covered, so a small model stays on track.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refuses secrets.&lt;/strong&gt; If you paste a password, card number, or seed phrase, a guard blocks it before it reaches the model or the disk. The letter says where things are ("password manager, emergency kit in the blue folder"), never what they are.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writes the letter from facts.&lt;/strong&gt; Every fact comes from a validated record, not from model free text. The model only helps with personal messages, and you approve everything before sealing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seals with open standards.&lt;/strong&gt; &lt;a href="https://github.com/FiloSottile/age" rel="noopener noreferrer"&gt;&lt;code&gt;age&lt;/code&gt;&lt;/a&gt; encryption, with the key split by SLIP-39 into 5 cards, any 3 of which unlock it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recovers without me.&lt;/strong&gt; The result opens with the stock &lt;code&gt;age&lt;/code&gt; tool, so the family does not need Afterlight at all. I confirmed this: recovery opens with the stock &lt;code&gt;age&lt;/code&gt; binary, no Afterlight code involved.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/sE5_2yS4G1I" 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/hendrahend" rel="noopener noreferrer"&gt;
        hendrahend
      &lt;/a&gt; / &lt;a href="https://github.com/hendrahend/afterlight" rel="noopener noreferrer"&gt;
        afterlight
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Afterlight: a local-first digital estate planner
    &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;Afterlight 🕯️&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Local-first digital estate planner — built for DEV Hacktoberfest "Ghost in the Machine" (Oct 26–31, 2026).&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Afterlight interviews you about your digital life, turns the answers into a plain-letter of instruction for your family, encrypts it with &lt;code&gt;age&lt;/code&gt;, and splits the key with SLIP-39 into 5 printable cards. Any 3 of them unlock the letter. Nothing leaves your machine.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/hendrahend/afterlight/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667" alt="MIT License"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why open&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This data is too sensitive for a cloud API — a local model is a &lt;strong&gt;requirement&lt;/strong&gt;, not a preference. The output must stay readable with standard tools (&lt;code&gt;age&lt;/code&gt; + any SLIP-39 implementation) even if this project disappears. The model is swappable through config.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Three reasons open matters here:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No cloud.&lt;/strong&gt; Your answers about accounts, devices, and wishes never touch a remote server. The local LLM never sends anything outward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interoperable output.&lt;/strong&gt; &lt;code&gt;estate.age&lt;/code&gt; is a standard &lt;code&gt;age&lt;/code&gt; file. The share cards are SLIP-39 mnemonics. Anyone…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hendrahend/afterlight" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;The central design choice is a hard split between what the model does and what plain code does.&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%2Fpr9qduixtvwpf8my62go.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%2Fpr9qduixtvwpf8my62go.png" alt="Afterlight create flow" width="800" height="309"&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%2Fwpbem5sgkv258rzx580k.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%2Fwpbem5sgkv258rzx580k.png" alt="Afterlight recover flow" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Purple boxes are where the open-weight model works. Everything else is plain, testable code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model:&lt;/strong&gt; &lt;code&gt;gemma3:4b&lt;/code&gt; (2.5 GB), served through &lt;strong&gt;Ollama&lt;/strong&gt; on a Windows laptop with no GPU. The backend speaks an OpenAI-compatible API, and the model name lives in a config file, not in the code. I compared one model — &lt;code&gt;gemma3:4b&lt;/code&gt; produced valid JSON on 5 of 5 answers at about 18 tokens/s, which was enough to move forward without a GPU.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The interview:&lt;/strong&gt; a state machine owns the checklist (accounts, money, devices, people, wishes). The model rephrases each question naturally and extracts answers into a JSON schema using constrained output. Pydantic validates the result. If the model fails, static fallback questions keep the interview going. A small model plus a strict scaffold turned out to be more reliable than a big model improvising.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The guard:&lt;/strong&gt; regex and entropy checks (Luhn for card numbers, seed-phrase patterns, high-entropy tokens) run on every answer before it goes anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The seal:&lt;/strong&gt; a random 128-bit secret becomes the &lt;code&gt;age&lt;/code&gt; passphrase, and the same secret is split with SLIP-39 (&lt;code&gt;shamir-mnemonic&lt;/code&gt;, Trezor's reference implementation) into 3-of-5 word shares with checksums, printed as cards with a QR code. A &lt;code&gt;RECOVERY.txt&lt;/code&gt; in the package explains, in plain language, how to open everything with only &lt;code&gt;age&lt;/code&gt; and any SLIP-39 tool.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Recover flow diagram coming once I capture the screenshots.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recovery needs no model at all, only open tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tests:&lt;/strong&gt; 163 tests. Seal and recover round-trip for every 3-of-5 combination, fail correctly with only 2 shares, catch a single checksum typo in a share word, reject guard samples (API keys, card numbers, seed phrases), and a test fails on any non-loopback network connection.&lt;/p&gt;

&lt;p&gt;I wrote the spec first in &lt;code&gt;AGENTS.md&lt;/code&gt; and built the code with opencode following it. I decided the architecture and the hard rules up front (local-first, no plaintext on disk, guard every answer, no non-loopback network); the agent implemented to that plan rather than inventing its own.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Privacy is the precondition, not a feature.&lt;/strong&gt; This is the most sensitive data a person owns: accounts, money, family, wishes. With a closed API, every answer in the interview would travel to someone else's server. With an open-weight model on my own laptop, nothing leaves. You do not have to take my word for it: the demo runs with Wi-Fi off, and the test suite fails if the code opens a non-local connection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This must outlive me.&lt;/strong&gt; A digital legacy tool that depends on a company is a promise that expires when the company does. Afterlight's output uses open standards with independent implementations, &lt;code&gt;age&lt;/code&gt; and SLIP-39. In thirty years, your family needs a copy of &lt;code&gt;age&lt;/code&gt; and the instructions in &lt;code&gt;RECOVERY.txt&lt;/code&gt;. They do not need this project, and they do not need me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model is swappable.&lt;/strong&gt; Changing the model is one line of config. That is what lets the same tool run on a modest laptop today and on whatever small model is best next year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It costs nothing to run,&lt;/strong&gt; which matters for something people should do once and not pay a subscription to keep safe.&lt;/p&gt;

&lt;p&gt;The honest tradeoff: a small local model is not as capable as a frontier model, so it cannot be trusted to improvise. That pushed me to give it a narrow job inside a deterministic structure, and I think the tool is safer for it.&lt;/p&gt;

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

&lt;p&gt;The first time I ran the interview on my own life, the questions were simple but the answers were not. "Where are your accounts?" — I could answer that. "Who should get the photo drive no one else knows about?" — I had to decide, and deciding meant admitting there was something to decide. The guard is the moment I respect most: I pasted a real-looking API key into the test on purpose, and it refused to seal. It would rather be a pain in the moment than a secret leaked in a letter. And when three cards opened the letter and two did not, the 3-of-5 math stopped being a theorem and became a promise I could actually keep for my family.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Sloth Sounds: I Was Too Lazy to Pick a Playlist, So I Taught My Laptop to Make the Music 🦥</title>
      <dc:creator>Hendra Hendriana</dc:creator>
      <pubDate>Thu, 22 Oct 2026 07:12:36 +0000</pubDate>
      <link>https://dev.to/hendrahend/sloth-sounds-i-was-too-lazy-to-pick-a-playlist-so-i-taught-my-laptop-to-make-the-music-57f9</link>
      <guid>https://dev.to/hendrahend/sloth-sounds-i-was-too-lazy-to-pick-a-playlist-so-i-taught-my-laptop-to-make-the-music-57f9</guid>
      <description>&lt;h1&gt;
  
  
  Sloth Sounds: I Was Too Lazy to Pick a Playlist, So I Taught My Laptop to Make the Music 🦥
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;This is my submission for the Hacktoberfest Open-Source AI Challenge, Week 3: Slow and Steady.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;International Sloth Day was October 20, and sloths have the right idea. They move at their own&lt;br&gt;
pace, conserve their energy, and let the world come to them. So for this week's challenge I&lt;br&gt;
built the most sloth-shaped thing I could think of: a tool for people who want music to wind&lt;br&gt;
down to but cannot be bothered to browse, curate, or pay for another subscription.&lt;/p&gt;

&lt;p&gt;You type a lazy vibe — "rainy sunday piano", "slow ambient for sleep", "fireplace while it&lt;br&gt;
snows" — and Sloth Sounds writes the music. One command gets you a seamless loop. One command&lt;br&gt;
before bed gets you a whole multi-minute wind-down set by morning.&lt;/p&gt;

&lt;p&gt;Everything runs on my laptop. Open-weight models only. Nothing leaves the machine.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;Sloth Sounds is a local-first ambient music generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"rainy sunday piano" ──► vibe expander ──► MusicGen (open weights) ──► seamless loop
  your lazy words        Gemma 3 / Ollama    facebook/musicgen-small    WAV + MP3
                         or keyword map      (swappable)                out/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;slothsounds gen "rainy sunday piano"&lt;/code&gt;&lt;/strong&gt; — generates one click-free loop in about half
a minute on my GPU (a 4 GB RTX 2050; it also runs on CPU, just slower)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;slothsounds night 10 "slow ambient for sleep"&lt;/code&gt;&lt;/strong&gt; — composes a 10-segment set, each
segment a variation on the vibe so the piece evolves instead of repeating, crossfaded
into one continuous file with a run sheet of timecodes and seeds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;slothsounds doctor&lt;/code&gt;&lt;/strong&gt; — tells you exactly what your machine has and what it's missing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack is entirely open:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Why open&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;MusicGen&lt;/strong&gt; (&lt;code&gt;facebook/musicgen-small&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;generates the audio&lt;/td&gt;
&lt;td&gt;open weights, runs offline, swappable, fine-tunable, free forever&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gemma 3 via Ollama&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;expands lazy vibes into music prompts&lt;/td&gt;
&lt;td&gt;runs on a laptop, no API key, no usage meter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;numpy + stdlib &lt;code&gt;wave&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;loops, crossfades, WAV I/O&lt;/td&gt;
&lt;td&gt;boring, reliable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;ffmpeg&lt;/strong&gt; (optional)&lt;/td&gt;
&lt;td&gt;MP3 export&lt;/td&gt;
&lt;td&gt;already on your machine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Gemma layer: from lazy words to music prompts
&lt;/h2&gt;

&lt;p&gt;MusicGen prompts want concrete audio descriptors. Humans type feelings. Gemma 3 (4B, Q4,&lt;br&gt;
via Ollama) sits in the middle and translates. Real outputs from tonight's run:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You type&lt;/th&gt;
&lt;th&gt;Gemma writes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rainy sunday piano&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ambient piano, melancholic, sustained chords, subtle rain ambience, gentle arpeggios, 60 bpm, seamless loop, no vocals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fireplace while it snows&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Warm synth pads, gentle piano, distant sleigh bells, crackling log, soft strings, 60 bpm, seamless loop, no vocals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;midnight train through the countryside&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Synth pads, distant steel drums, mournful cello, driving arpeggio, shimmering reverb, slow tempo, expansive atmosphere, seamless loop, no vocals&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If Ollama isn't running, a keyword-based expander takes over so the tool still works fully&lt;br&gt;
offline. &lt;code&gt;slothsounds doctor&lt;/code&gt; reports which path is live.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hear the Gemma layer work
&lt;/h2&gt;

&lt;p&gt;Same vibe, same seed (777), same model, same GPU. The only variable is who writes the prompt:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Expander&lt;/th&gt;
&lt;th&gt;What MusicGen received&lt;/th&gt;
&lt;th&gt;Listen&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Keyword heuristic&lt;/td&gt;
&lt;td&gt;rainy sunday piano, gentle rainfall, distant rolling thunder, soft felt-piano melody, intimate close-miked piano, ambient instrumental, seamless loop, high quality recording, no vocals&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/hendrahend/slothsounds/blob/main/site/audio/rainy-sunday-piano-777-heuristic.wav" rel="noopener noreferrer"&gt;heuristic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 3&lt;/td&gt;
&lt;td&gt;Ambient piano, melancholic, subtle strings, gentle rain ambience, slow tempo, shimmering textures, seamless loop, no vocals&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/hendrahend/slothsounds/blob/main/site/audio/rainy-sunday-piano-777-gemma.wav" rel="noopener noreferrer"&gt;gemma&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The heuristic path staples stock descriptors onto your words. Gemma makes decisions — it&lt;br&gt;
picks the instrumentation, sets a tempo, shapes an arc. Same seed means any difference you&lt;br&gt;
hear comes from the prompt alone.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Three loops and one night set, all generated on my RTX 2050 tonight, committed as WAVs in&lt;br&gt;
the repo so you can listen without running anything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/hendrahend/slothsounds/blob/main/site/audio/rainy-sunday-piano-42.wav" rel="noopener noreferrer"&gt;&lt;code&gt;rainy-sunday-piano-42.wav&lt;/code&gt;&lt;/a&gt; — 30s loop&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hendrahend/slothsounds/blob/main/site/audio/fireplace-while-it-snows-101.wav" rel="noopener noreferrer"&gt;&lt;code&gt;fireplace-while-it-snows-101.wav&lt;/code&gt;&lt;/a&gt; — 30s loop&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hendrahend/slothsounds/blob/main/site/audio/midnight-train-through-the-countryside-202.wav" rel="noopener noreferrer"&gt;&lt;code&gt;midnight-train-through-the-countryside-202.wav&lt;/code&gt;&lt;/a&gt; — 30s loop&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hendrahend/slothsounds/blob/main/site/audio/full-set.wav" rel="noopener noreferrer"&gt;&lt;code&gt;full-set.wav&lt;/code&gt;&lt;/a&gt; — 4.7-minute night set (10 segments, crossfaded)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Live demo: &lt;a href="https://slothsounds-site.onrender.com/" rel="noopener noreferrer"&gt;slothsounds-site.onrender.com&lt;/a&gt;&lt;/strong&gt; — a&lt;br&gt;
player page with all six tracks, deployed on Render from the &lt;code&gt;render.yaml&lt;/code&gt; blueprint in&lt;br&gt;
the repo. Generation stays local; the site is just the shop window.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;slothsounds doctor&lt;/code&gt; on my machine, verbatim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[OK  ] Python &amp;gt;= 3.10  3.12.10
[OK  ] numpy           2.5.4
[OK  ] torch           2.5.1+cu121 (NVIDIA GeForce RTX 2050)
[OK  ] audiocraft      1.3.0
[--  ] ffmpeg          not found (optional, MP3 export only)
[OK  ] ollama          reachable, models: gemma3:4b
[--  ] backboard       SLOTHSOUNDS_BACKBOARD_KEY not set (optional, taste memory)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why open innovation matters for what I built
&lt;/h2&gt;

&lt;p&gt;The closed-API version of this project would be a wrapper: send your vibe to someone else's&lt;br&gt;
GPU, pay per generation, hope the service still exists next year, and hand a company a log of&lt;br&gt;
everything you listen to at 2 AM. That's not a tool, that's a subscription with extra steps.&lt;/p&gt;

&lt;p&gt;Running open-weight models locally means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It works offline.&lt;/strong&gt; Plane, cabin, cabin-in-the-woods. The music is generated where you are.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run.&lt;/strong&gt; The thousandth loop costs exactly as much as the first: zero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model is swappable.&lt;/strong&gt; &lt;code&gt;musicgen-small&lt;/code&gt; on a 4 GB laptop GPU today, &lt;code&gt;musicgen-medium&lt;/code&gt;
on a desktop tomorrow, a fine-tuned specialist the day after — the pipeline doesn't change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing is logged.&lt;/strong&gt; Your 2 AM listening habits stay between you and your laptop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The open pieces aren't decoration here — they're the whole product. Strip them out and there&lt;br&gt;
is no project left.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lazy payoff
&lt;/h2&gt;

&lt;p&gt;The challenge asked how much time or effort the project actually saves. Here's the honest&lt;br&gt;
math from my own evenings, measured on the runs above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The old ritual:&lt;/strong&gt; open the streaming app, scroll, skip, second-guess, land on something
you've already played. Five to ten minutes of decisions, every evening, forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The new ritual:&lt;/strong&gt; one command, about ten seconds of typing. The music exists half a
minute later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Night mode:&lt;/strong&gt; the decision happens before you're even tired. Ten segments — 4.7 minutes
of evolving music — composed in about five and a half minutes of wall time. You kick it
off, and the set is just there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Measured on the RTX 2050: each 30-second segment takes roughly 32 seconds to generate, and&lt;br&gt;
the full 10-segment set (282 seconds of audio after crossfades) finished in about five and a&lt;br&gt;
half minutes. Gemma's vibe expansion adds a few seconds when the model is warm.&lt;/p&gt;

&lt;p&gt;I won't pretend this saves anyone hours. What it actually removes is the deciding — the&lt;br&gt;
scrolling, the skipping, the standing in the kitchen at 11 PM arguing with yourself about&lt;br&gt;
what to play. The sloth doesn't optimize the walk. The sloth skips it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize categories
&lt;/h2&gt;

&lt;p&gt;Entering two categories, where the integration is strongest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — Gemma 3 (via Ollama) expands lazy vibes into MusicGen-optimized
prompts, with an offline keyword fallback; it also teachers the fine-tuning dataset in
the repo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render&lt;/strong&gt; — the public demo site at
&lt;a href="https://slothsounds-site.onrender.com/" rel="noopener noreferrer"&gt;slothsounds-site.onrender.com&lt;/a&gt;, deployed from
the &lt;code&gt;render.yaml&lt;/code&gt; blueprint in the repo, serving the actual generated audio&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tinker and Backboard integrations are wired in the repo (a Gemma-distilled training&lt;br&gt;
dataset plus LoRA SFT script; taste memory with a local fallback), but they are not the&lt;br&gt;
headline entries for this challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show your work
&lt;/h2&gt;

&lt;p&gt;Repo: &lt;strong&gt;github.com/hendrahend/slothsounds&lt;/strong&gt; (MIT). 21 tests, &lt;code&gt;slothsounds doctor&lt;/code&gt; for&lt;br&gt;
setup checks, and a Windows shim script because audiocraft's xformers wheels don't exist&lt;br&gt;
for Windows — the kind of detail you only find by actually running the thing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built during the Week 3 window (Oct 19–25). Repo: github.com/hendrahend/slothsounds&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hf26challenge</category>
      <category>devchallenge</category>
      <category>gemma</category>
      <category>ai</category>
    </item>
    <item>
      <title>The hundredth clip costs nothing: Klipkit, a local-first clip finder for freelance editors</title>
      <dc:creator>Hendra Hendriana</dc:creator>
      <pubDate>Sun, 18 Oct 2026 03:03:30 +0000</pubDate>
      <link>https://dev.to/hendrahend/the-hundredth-clip-costs-nothing-klipkit-a-local-first-clip-finder-for-freelance-editors-5891</link>
      <guid>https://dev.to/hendrahend/the-hundredth-clip-costs-nothing-klipkit-a-local-first-clip-finder-for-freelance-editors-5891</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;Klipkit&lt;/strong&gt; is a clip finder for freelance short-form editors that runs entirely on a laptop — no upload, no per-minute API bill, no NDA problem.&lt;/p&gt;

&lt;p&gt;Point it at a podcast, a stream VOD or a client's raw footage. It transcribes the audio locally, finds the moments that work as standalone clips, writes a hook line for each, and exports a client-ready pack: MP4 + &lt;code&gt;.srt&lt;/code&gt; + a run sheet with timecodes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source ──► whisper (local) ──► candidate moments ──► local LLM judge ──► clip pack
file/URL     word timestamps     cheap heuristics      open-weight model     mp4 + srt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Who would pay for it:&lt;/strong&gt; freelance clippers and short-form editors who get paid per clip and work under NDA. They have two problems that hosted tools make worse.&lt;/p&gt;

&lt;p&gt;Per-minute pricing: cloud clip tools charge by upload-minute. If a client sends a two-hour podcast and you pull eight clips, you pay for the whole two hours. Do that for ten clients and the tooling costs more than the job.&lt;/p&gt;

&lt;p&gt;And the footage isn't theirs. Clients send raw footage under NDA — uploading it to a server the client doesn't control is a non-starter, and "we have a SOC 2 report" is not a satisfying answer when the contract says the file never leaves the editor's machine.&lt;/p&gt;

&lt;p&gt;So the business only works if the marginal cost of the hundredth clip is zero. That requires local inference, which requires open weights.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;No hosted link, and that's the point — Klipkit runs locally, so there is nothing to deploy. Here is a real run on a public 62-minute Indonesian-language talk video, on an RTX 2050 with 4 GB of VRAM:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ klipkit run "https://www.youtube.com/watch?v=zOnA_8JmdM8" -n 5 --whisper small --llm gemma3:1b

  Top moments - NEPHILIM, NABI ADAM &amp;amp; RAKSASA PURBA...
┏━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━┳━━━━━━━┳━━━━━━┓
┃ # ┃ Start    ┃ End      ┃ Len ┃ Score ┃ Hook ┃
┡━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━╇━━━━━━━╇━━━━━━┩
│ 1 │ 00:02:45 │ 00:03:31 │ 46s │  0.91 │ -    │
│ 2 │ 00:03:37 │ 00:04:23 │ 46s │  0.91 │ -    │
│ 3 │ 00:14:16 │ 00:15:05 │ 49s │  0.91 │ -    │
│ 4 │ 00:19:45 │ 00:20:31 │ 46s │  0.91 │ -    │
│ 5 │ 00:49:41 │ 00:50:31 │ 50s │  0.91 │ -    │
└───┴──────────┴──────────┴─────┴───────┴──────┘

Client-ready pack: out/nephilim/pack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Test source is a public YouTube video, used only as input. Klipkit processed it locally — nothing was re-uploaded, and the rendered clips stay on my machine.&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&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;Transcribe&lt;/td&gt;
&lt;td&gt;9,612 words, language &lt;code&gt;id&lt;/code&gt; at 0.99 confidence, ~6 min on GPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utterances&lt;/td&gt;
&lt;td&gt;1,216, reassembled from word timings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates&lt;/td&gt;
&lt;td&gt;606 → 16 shortlisted by heuristics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;5 MP4 clips (45–50 s), 5 SRT files, a run sheet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Roughly ten minutes of wall clock, six of it transcription. The SRT timings come out of the &lt;em&gt;same&lt;/em&gt; word timings used for cutting, so captions stay locked to the audio instead of drifting.&lt;/p&gt;

&lt;p&gt;The empty &lt;code&gt;Hook&lt;/code&gt; column is the honest part of this demo, and I get into it below.&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/hendrahend" rel="noopener noreferrer"&gt;
        hendrahend
      &lt;/a&gt; / &lt;a href="https://github.com/hendrahend/klipkit" rel="noopener noreferrer"&gt;
        klipkit
      &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;Klipkit&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Find the clips worth posting — on your own laptop, with no cloud in the loop.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Klipkit is a local-first clip finder built for freelance clippers and short-form video editors
Point it at a podcast, a stream VOD or a client's raw footage; it transcribes the audio with
Whisper, finds the moments that actually work as standalone clips, writes a hook line for each
and exports a client-ready pack (MP4 + &lt;code&gt;.srt&lt;/code&gt; captions + a notes sheet).&lt;/p&gt;
&lt;p&gt;Nothing leaves your machine. No upload, no per-minute API bill, no NDA problem.&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;                                                    
  source ──► whisper (local) ──► candidate moments ──► local LLM judge ──► clip pack
  file/URL     word timestamps     cheap heuristics      open-weight model     mp4 + srt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why local, and why open&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Freelance clippers are paid per clip, so two things kill their margin: &lt;strong&gt;time&lt;/strong&gt; and &lt;strong&gt;API cost&lt;/strong&gt;
Cloud clip tools charge per upload-minute, which makes high-volume work unprofitable, and they…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hendrahend/klipkit" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;MIT licensed. Python, with &lt;code&gt;faster-whisper&lt;/code&gt;, Ollama, &lt;code&gt;ffmpeg&lt;/code&gt; and &lt;code&gt;yt-dlp&lt;/code&gt; as the only real dependencies.&lt;/p&gt;

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

&lt;p&gt;The open-source AI is the product, not a decoration. Four open pieces do all the work:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Why it has to be open&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;faster-whisper&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;transcription with word-level timestamps&lt;/td&gt;
&lt;td&gt;runs offline, no per-minute fee, audio never leaves the disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ollama + Gemma 3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;judges which moments are clip-worthy, writes the hooks&lt;/td&gt;
&lt;td&gt;runs on a laptop GPU, swappable, free forever&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ffmpeg&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;cutting and caption rendering&lt;/td&gt;
&lt;td&gt;already on every editor's machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;yt-dlp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;optional URL import&lt;/td&gt;
&lt;td&gt;no vendor gatekeeping your sources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The design decision that mattered most: &lt;strong&gt;the model never sees the whole transcript.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An hour of talk audio is ~10,000 words. Feeding that to a small open-weight model in one shot produces garbage — it loses the thread and burns context on filler. So the pipeline layers cheap and expensive. Heuristics run first and cost nothing: overlapping 45-second windows are scored on question openers, digits, filler density, and whether the cut starts mid-thought. Only the top slice reaches the model.&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="c1"&gt;# klipkit/text.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rank_candidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keep_ratio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_keep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_keep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ranked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;heuristic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;keep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_keep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;keep_ratio&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;keep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_keep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ranked&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ranked&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;keep&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That cap does real work: 606 candidates became 16, which is four small batches of judging instead of 606. Ollama runs the judge with &lt;code&gt;format: "json"&lt;/code&gt; so the model is constrained to a parseable reply, and the whole pipeline ran on a 4 GB laptop GPU.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What broke on the way&lt;/strong&gt; — the parts I'd want to read before trying this myself:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. yt-dlp now requires a JavaScript runtime.&lt;/strong&gt; Since the 2026 releases, YouTube extraction needs one, and it degrades with a vague "some formats may be missing" warning instead of an error. Node was already installed; the Python API just wasn't being told about it.&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="c1"&gt;# klipkit/ingest.py
&lt;/span&gt;&lt;span class="n"&gt;opts&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;format&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;bv*[height&amp;lt;=720][ext=mp4]+ba[ext=m4a]/b[height&amp;lt;=720]/bv*[height&amp;lt;=720]+ba/b&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;js_runtimes&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node&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="c1"&gt;# without this, YouTube extraction silently degrades
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;av&lt;/code&gt; 19 broke faster-whisper.&lt;/strong&gt; Not a warning — a hard crash on every input, because &lt;code&gt;av.open()&lt;/code&gt; gained a &lt;code&gt;metadata_errors&lt;/code&gt; keyword that faster-whisper 1.2.1 doesn't pass. The fix belongs in &lt;code&gt;pyproject.toml&lt;/code&gt;, not a README footnote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="py"&gt;"faster-whisper&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="s"&gt;"av&amp;lt;15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="py"&gt;"yt-dlp&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2025.1&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. cuBLAS wasn't on &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt;.&lt;/strong&gt; faster-whisper picked CUDA via &lt;code&gt;device="auto"&lt;/code&gt;, model construction succeeded, then encoding failed with &lt;code&gt;Library libcublas.so.12 is not found&lt;/code&gt;. The library existed — bundled inside Ollama's own runtime. Pointing &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; at it took the GPU to 98% utilization. Worth knowing before concluding your GPU is unsupported.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Gemma 3 1b is too small to batch-score reliably.&lt;/strong&gt; This is the empty Hook column. I asked it to score four candidates at once and return JSON; it returned verdicts for one or two, and one batch came back as an HTTP 500 from Ollama. Every clip fell back to heuristic scoring.&lt;/p&gt;

&lt;p&gt;That's a model-capability limit, not a pipeline bug. The default is &lt;code&gt;gemma3:4b&lt;/code&gt; for a reason — I only had the 1b because the Ollama registry was unreachable from this machine and I imported the GGUF from HuggingFace directly. It's also the clearest argument for the swappable-model design: the fix is a flag, not a rewrite.&lt;/p&gt;

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

&lt;p&gt;Three things about this project only exist because the pieces are open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The marginal cost is actually zero.&lt;/strong&gt; A closed API that charges per minute makes high-volume freelance work unprofitable. Running Gemma 3 locally means the hundredth clip costs the same as the first: nothing. That isn't a discount, it's the difference between a tool a freelancer can build a business on and a tool that eats their margin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The client's footage never leaves the laptop.&lt;/strong&gt; This is a hard requirement, not a preference. No closed API can satisfy an NDA clause that says the file stays on the editor's machine — no matter how good its privacy policy is. Local open-weight inference is the only architecture that makes the promise structurally true instead of contractually promised.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I can swap the brain without touching the pipeline.&lt;/strong&gt; &lt;code&gt;--llm gemma3:1b&lt;/code&gt; for a fast pass, &lt;code&gt;gemma3:4b&lt;/code&gt; when a clip really matters, or something else entirely next month. When I discovered the 1b model couldn't batch-score, the fix was one flag. With a hosted API, model quality is a vendor decision I'd have to wait on.&lt;/p&gt;

&lt;p&gt;The open pieces are also what let a 1b model and a 4 GB GPU be enough. I'm not paying per token, so layering a free heuristic prefilter in front of an expensive judge is pure upside — 606 candidates down to 16 before the model sees anything.&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 is the judge at the centre of the pipeline, not a bolt-on. It scores hook strength, self-containment, payoff and energy for each shortlisted moment and writes the on-screen hook line. The project ships with &lt;code&gt;gemma3:4b&lt;/code&gt; as the default and treats the model as swappable, so the same code runs a 1b or a larger Gemma depending on the hardware and how much the clip matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Wisdom
&lt;/h2&gt;

&lt;p&gt;The local-LLM stack I leaned on is well-trodden on DEV, and these shaped how I built it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/shaik_idris_44638c7ce9825/build-a-voice-controlled-local-ai-agent-with-ollama-and-faster-whisper-5b18"&gt;Build a Voice-Controlled Local AI Agent with Ollama and Faster-Whisper&lt;/a&gt; by Shaik Idris — the Whisper + Ollama pairing that made this feel buildable on a laptop.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/zackriya/local-meeting-notes-with-whisper-transcription-ollama-summaries-gemma3n-llama-mistral--2i3n"&gt;Local Meeting Notes with Whisper Transcription + Ollama Summaries (Gemma3n, LLaMA, Mistral) — Meetily AI&lt;/a&gt; by Zackriya — proof that local transcription plus a local summarizer is a real product shape, not a demo.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/nearshi/-clean-json-extraction-with-ollama-and-python-21f3"&gt;Clean JSON Extraction with Ollama and Python&lt;/a&gt; by nearshi — the parsing discipline I should have applied before discovering the 1b model's partial responses the hard way.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/toai/taming-local-llm-non-determinism-building-a-resilient-multi-stage-json-sanitizer-schema-adapter-ebg"&gt;Taming Local LLM Non-Determinism: Building a Resilient, Multi-Stage JSON Sanitizer &amp;amp; Schema Adapter in Python&lt;/a&gt; by toai — the repair-validate-retry pattern that's now first on my list.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Score one candidate at a time and accept the latency.&lt;/strong&gt; Batching looked like an obvious token saving, but a 1b model can't hold four scoring rubrics in its head at once, and a partial JSON response is worse than a slow complete one — the fallback silently degrades quality instead of failing loudly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make the fallback visible.&lt;/strong&gt; "scored by heuristics only" is currently a note in the run sheet. It should be a warning at the top, because a pack with five empty hook columns looks like a working result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test on a short source first.&lt;/strong&gt; My first real test was 62 minutes, so every iteration cost six minutes of transcription. A three-minute clip would have caught the &lt;code&gt;av&lt;/code&gt; crash in seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Talk-heavy footage is where this shines. B-roll-driven video has no transcript worth judging.&lt;/li&gt;
&lt;li&gt;Auto-reframing to 9:16 is deliberately not in v1 — a bad vertical crop is worse than none. Klipkit hands the editor timecodes and captions and lets them reframe in CapCut.&lt;/li&gt;
&lt;li&gt;Non-Latin scripts and heavy code-switching depend on Whisper's coverage.&lt;/li&gt;
&lt;li&gt;No first user yet. The next step is handing it to an editor with a real client backlog and watching which flag they reach for first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run it yourself: &lt;code&gt;klipkit doctor&lt;/code&gt; tells you what's missing before you spend six minutes finding out.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>python</category>
    </item>
    <item>
      <title>Kicau: an Indonesian bird-call identifier that works offline</title>
      <dc:creator>Hendra Hendriana</dc:creator>
      <pubDate>Sun, 11 Oct 2026 15:32:43 +0000</pubDate>
      <link>https://dev.to/hendrahend/kicau-an-indonesian-bird-call-identifier-that-works-offline-2ica</link>
      <guid>https://dev.to/hendrahend/kicau-an-indonesian-bird-call-identifier-that-works-offline-2ica</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;The birds outside my window have Indonesian names — but every bird-identifying app I tried only knew the English ones. Six apps, and not one could tell me what the bird out there is called in the language I actually speak.&lt;/p&gt;

&lt;p&gt;So I built Kicau: an offline bird-call identifier that speaks Indonesian.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Kicau&lt;/strong&gt; records bird audio — from your microphone or an uploaded file — and identifies the species, on-device, in English or Indonesian. No internet, no server, no account. It works in the middle of a national park with zero bars of signal, and your recordings never leave your device. An EN/ID toggle in the corner switches the whole UI &lt;em&gt;and&lt;/em&gt; the species names — &lt;em&gt;Orthotomus sutorius&lt;/em&gt; reads "Common Tailorbird" or "Cinenen pisang" depending on the toggle.&lt;/p&gt;

&lt;p&gt;It's for Indonesian birders, hikers, and anyone who's ever heard a bird on a trail and wondered what it was. The screen is the shortest part of the experience: record ten seconds, read the result, pocket the phone, go find the bird. Tap "Use my location" and it also weighs which birds actually live near you.&lt;/p&gt;

&lt;p&gt;Yes — there are other bird identifiers in this challenge. Kicau is the one built for Indonesia's 280 million people: Bahasa Indonesia UI, Indonesian species names ("Cikrak bambu", not just &lt;em&gt;Abroscopus superciliaris&lt;/em&gt;), two pip packages, one HTML file, zero build step.&lt;/p&gt;

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

&lt;p&gt;Kicau is offline-first, so there's no hosted demo — that would defeat the point. It runs on your machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/hendrahend/kicau &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;kicau
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python scripts/download_model.py   &lt;span class="c"&gt;# one-time, ~53 MB&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; kicau.server             &lt;span class="c"&gt;# open http://127.0.0.1:8765&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fhlctawhfjrx7ax7hkstk.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%2Fhlctawhfjrx7ax7hkstk.png" alt="Kicau main UI — record and upload tabs" width="480" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Common Tailorbird field recording, identified on-device:&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%2Fraw.githubusercontent.com%2Fhendrahend%2Fkicau%2Fmain%2Fdocs%2Fdemo-result.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%2Fraw.githubusercontent.com%2Fhendrahend%2Fkicau%2Fmain%2Fdocs%2Fdemo-result.png" alt="Kicau result — species cards with confidence bars and the per-3-second detection timeline" width="480" height="1310"&gt;&lt;/a&gt;&lt;br&gt;
Or from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; kicau.identify recording.mp3 &lt;span class="nt"&gt;--top-n&lt;/span&gt; 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;a href="https://github.com/hendrahend/kicau" rel="noopener noreferrer"&gt;https://github.com/hendrahend/kicau&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MIT licensed. What's inside:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kicau/identify.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Model loading, chunking, inference, Indonesian labels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kicau/audio.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ffmpeg-based decode/resample to 48 kHz mono&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kicau/server.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;stdlib-only HTTP server + &lt;code&gt;/api/identify&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kicau/labels_id.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;6,522 species labels in Indonesian (BirdNET team)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;web/index.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single-file offline UI — record, upload, timeline, playback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tests/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pipeline tests, including a real-audio classification test&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;The whole experience is built around one open-weight model:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;BirdNET v2.4&lt;/strong&gt; (Cornell Lab of Ornithology / TU Chemnitz)&lt;/td&gt;
&lt;td&gt;6,522-species bioacoustics classifier; I use the fp16 TFLite variant (~26 MB)&lt;/td&gt;
&lt;td&gt;CC BY-NC 4.0 (weights)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;LiteRT&lt;/strong&gt; (&lt;code&gt;ai-edge-litert&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;On-device inference runtime — no TensorFlow install, no GPU&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BirdNET's Indonesian labels&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Species names in Bahasa Indonesia, shipped by the BirdNET team&lt;/td&gt;
&lt;td&gt;MIT (analyzer repo)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One honest caveat on the Indonesian names: the BirdNET team's Indonesian label set covers 1,227 of the 6,522 species so far — the rest fall back to English while the translation grows. The covered ones include the birds you're most likely to meet in Indonesia (Cinenen, Cikrak, Elang-alap).&lt;/p&gt;

&lt;p&gt;Pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mic / upload → ffmpeg (→ 48 kHz mono) → 3-second chunks →
BirdNET v2.4 TFLite (LiteRT, on-device) → flat sigmoid →
top species per chunk, Indonesian names
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I verified the math against BirdNET-Analyzer's source line by line — chunk length, sample rate, sigmoid sensitivity — so results match the reference implementation. Then I ran BirdNET's own reference soundscape through it: Black-capped Chickadee 0.92, Dark-eyed Junco 0.88, House Finch 0.83. And on an openly-licensed field recording of a Common Tailorbird (Vis M via iNaturalist, CC BY-SA 4.0, Wikimedia Commons): &lt;strong&gt;"Common Tailorbird (&lt;em&gt;Orthotomus sutorius&lt;/em&gt;)" at 1.00 confidence&lt;/strong&gt; — correct species. (Flip the app's EN/ID toggle and it reads "Cinenen pisang" — species names switch languages too.)&lt;/p&gt;

&lt;p&gt;One honest miss, because that's part of the deal: on a noisy caged-bird training clip, confidence stayed low (0.06–0.21) and the top guess was wrong. Clean field recordings are this model's home turf; noisy clips aren't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Location-aware, too.&lt;/strong&gt; BirdNET ships a second tiny model that takes your latitude, longitude, and the week of the year, and returns how likely each species is there right now. Tap "Use my location" and Kicau multiplies the acoustic confidence by that local likelihood — a bird that merely sounds similar but doesn't live here gets demoted. I verified it: in Jakarta in October the top picks are Burung-gereja erasia (Eurasian Tree Sparrow) and Perkutut jawa (Zebra Dove) — exactly the birds outside my window — while Berlin returns Great Tit and Eurasian Blackbird.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No signal, no problem.&lt;/strong&gt; The entire point is identifying birds &lt;em&gt;outside&lt;/em&gt;. A cloud API is useless in a national park. A local open-weight model is the only architecture that works where the birds actually are.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your recordings stay yours.&lt;/strong&gt; Audio is decoded, chunked, classified, and deleted — on your machine. There is nowhere to upload it to, because there is no server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing, forever.&lt;/strong&gt; No per-request pricing, no API key to babysit. A school birding club can run it on one laptop for years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could actually verify it.&lt;/strong&gt; I checked my preprocessing against the reference implementation line by line and tested on known audio. Try getting that level of verification from a black-box API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One honest license note: the BirdNET &lt;em&gt;weights&lt;/em&gt; are CC BY-NC 4.0 (free for research, education, and personal use) while the analyzer code is MIT and my code is MIT. Open weights with a non-commercial term — worth knowing, and fine for this project.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of GitHub Copilot&lt;/strong&gt;: the repo ships a GitHub Actions CI workflow that installs dependencies, downloads the BirdNET weights, and runs the full pytest suite — including a real-audio classification test — on every push.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built for Hacktoberfest 2026. Then go outside. That's the whole point.&lt;/em&gt; 🌿&lt;/p&gt;

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