<?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: Nelge-3D</title>
    <description>The latest articles on DEV Community by Nelge-3D (@nelge3d).</description>
    <link>https://dev.to/nelge3d</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%2F4203440%2Fc50003f8-daff-420a-be94-0e0e202ca195.jpg</url>
      <title>DEV Community: Nelge-3D</title>
      <link>https://dev.to/nelge3d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nelge3d"/>
    <language>en</language>
    <item>
      <title>Snap Me: learn English from the objects around you, with Gemma 4 and a voice that runs in your browser</title>
      <dc:creator>Nelge-3D</dc:creator>
      <pubDate>Sun, 25 Oct 2026 12:03:44 +0000</pubDate>
      <link>https://dev.to/nelge3d/snap-me-learn-english-from-the-objects-around-you-with-gemma-4-and-a-voice-that-runs-in-your-4ie6</link>
      <guid>https://dev.to/nelge3d/snap-me-learn-english-from-the-objects-around-you-with-gemma-4-and-a-voice-that-runs-in-your-4ie6</guid>
      <description>&lt;p&gt;In Libreville, private English lessons are out of reach for many families, and word lists need a discipline that few beginners keep. I teach English, and I know one thing works better than lists: a word stuck to an object you actually own.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Snap Me&lt;/strong&gt; in about a day for the Hacktoberfest Hack Day Libreville 2026 (Microsoft User Group Gabon, theme "AI belongs to everyone"). You photograph an object, and the app gives you its English word, its pronunciation, three example sentences and two fill-in-the-blank exercises. A voice says the word out loud. Every exercise you get right earns XP, which unlocks the past and then the future tense.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try it: &lt;a href="https://snap-me-omega.vercel.app" rel="noopener noreferrer"&gt;https://snap-me-omega.vercel.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/Nelge-3D/snap-me" rel="noopener noreferrer"&gt;https://github.com/Nelge-3D/snap-me&lt;/a&gt;
&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%2Fro9e85ev1xz7229c36ht.jpg" 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%2Fro9e85ev1xz7229c36ht.jpg" alt="An identified object with its English word, pronunciation and example sentences" width="493" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two open models, no backend
&lt;/h2&gt;

&lt;p&gt;The whole AI chain relies on open models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4&lt;/strong&gt; looks at the photo and writes the lesson. I call it through the Gemini API, with the key kept on the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kokoro-82M&lt;/strong&gt; generates the voice. It runs entirely in the browser through Transformers.js, so no audio ever leaves the device, and the app falls back to &lt;code&gt;speechSynthesis&lt;/code&gt; if Kokoro is not available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no database and no account. Progress, XP, the daily quota and the notebook all live in &lt;code&gt;localStorage&lt;/code&gt;. That was a constraint I chose to stay shippable in a day, and it turned out to fit the use case well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making Gemma 4 fast enough
&lt;/h2&gt;

&lt;p&gt;My first version took 9 to 13 seconds to identify an object and 34 to 66 seconds to generate the lesson. Nobody waits that long after taking a photo. Two changes fixed it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Split the work in two calls.&lt;/strong&gt; The first call only sees the image and returns a few tokens (word, French translation, pronunciation), so the word appears right away. A second, text-only call then writes the sentences and exercises in the background.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn off the model's internal reasoning.&lt;/strong&gt; The documented &lt;code&gt;thinkingBudget: 0&lt;/code&gt; returns a 400 error on this model. I found &lt;code&gt;thinkingLevel: MINIMAL&lt;/code&gt; on a developer forum instead:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FAST_CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;thinkingConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;thinkingLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ThinkingLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MINIMAL&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With it, identification takes about 3 to 4 seconds and the lesson about 5 to 6 seconds, and the JSON stayed valid on every call I made. One caveat: this parameter is not in the official documentation, and I only validated it on a handful of calls, so I treat it as a tuning I would re-check before relying on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designed for slow, expensive data
&lt;/h2&gt;

&lt;p&gt;Photos are resized in the browser before anything is sent: a 512px version goes to the API, and a 768px version is kept for the on-screen result and the notebook. Sending a multi-megabyte photo on every snap would be a product bug where mobile data is costly. For the same reason the voice runs locally instead of calling a server on every listen, and the app is limited to 5 photos a day, which also nudges learners to pick an object that matters to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What small models teach you
&lt;/h2&gt;

&lt;p&gt;Two bugs from real use, both fixed with prompt changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A photo of a team around their laptops came back as "laptop". My prompt told the model to prefer objects over people. Now, when a photo is mainly several people, it names the group ("team", "family", "friends") and keeps preferring an object for a single person.&lt;/li&gt;
&lt;li&gt;One run returned the pronunciation of the French word instead of the English one. The prompt now says the pronunciation is for the English word, with examples, and the app always displays it between slashes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lesson: a small model has no guaranteed structured output, so I validate every field it returns and fix the format on my side instead of trusting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Exporting and importing progress to change device, recognizing several objects in one photo, and a spaced-repetition review mode for the notebook.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI disclosure
&lt;/h2&gt;

&lt;p&gt;I built this project with the help of Claude Code (Anthropic), and I directed the product, reviewed the changes and tested the app on real photos. Gemma 4 and Kokoro-82M run inside the app. The design reference mockups in the repository (&lt;code&gt;docs/references&lt;/code&gt;) were generated with AI and only served as design inspiration, they are not used in the app. The logo, icons and share image are drawn in code, and the screenshots are real captures of the app. I drafted this post with AI assistance and edited it myself.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>nextjs</category>
      <category>webdev</category>
      <category>hacktoberfest</category>
    </item>
  </channel>
</rss>
