<?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: W. van Kuipers</title>
    <description>The latest articles on DEV Community by W. van Kuipers (@wvankuipers).</description>
    <link>https://dev.to/wvankuipers</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%2F9523%2F3356735.jpeg</url>
      <title>DEV Community: W. van Kuipers</title>
      <link>https://dev.to/wvankuipers</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wvankuipers"/>
    <language>en</language>
    <item>
      <title>TopoHooks: Gemma turns country names into sound-alike pictures for dyslexic kids</title>
      <dc:creator>W. van Kuipers</dc:creator>
      <pubDate>Thu, 15 Oct 2026 17:28:01 +0000</pubDate>
      <link>https://dev.to/wvankuipers/topohooks-gemma-turns-country-names-into-sound-alike-pictures-for-dyslexic-kids-50ib</link>
      <guid>https://dev.to/wvankuipers/topohooks-gemma-turns-country-names-into-sound-alike-pictures-for-dyslexic-kids-50ib</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/devteam/join-the-hacktoberfest-open-source-ai-challenge-side-hustle-2550-in-prizes-across-18-winners-55ba"&gt;Hacktoberfest Open-Source AI Challenge: Side Hustle&lt;/a&gt;, in the Best Use of Gemma category.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;My son is ten and dyslexic (as am I). Learning the countries and capitals of Europe means reading and remembering long strings of letters that mean nothing to him. What he does remember is silly pictures. In Dutch "Bulgarije" is &lt;strong&gt;bul + ga rijen&lt;/strong&gt;: a graduation hat on top of a car. He does not have to remember the spelling, he has to remember the car with the hat.&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%2Fhlivcoftuf6mf0gv5e09.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%2Fhlivcoftuf6mf0gv5e09.jpg" alt="Hat" width="800" height="600"&gt;&lt;/a&gt;&lt;br&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%2Ftm21tfjgqunw1p0vx1k6.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%2Ftm21tfjgqunw1p0vx1k6.jpg" alt="Car" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TopoHooks&lt;/strong&gt; is a small web app built around that trick:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Map.&lt;/strong&gt; An interactive map of Europe. Tap a country and see its name, its capital and its picture hook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cards.&lt;/strong&gt; The map shows a country, he thinks of the name, flips the card and sees the name, hears it read aloud, and sees the picture hook ("Bul → hat, ga rije → car"). A card he has never seen shows the answer straight away, because you cannot recall what you were never told.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practise.&lt;/strong&gt; A quiz with three answers where the wrong answers are the &lt;em&gt;neighbouring&lt;/em&gt; countries, because those are the ones that get confused. What he gets wrong comes back more often; what he gets right comes back less often.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Made for dyslexia.&lt;/strong&gt; Lexend or OpenDyslexic with one switch, short sentences, a listen button on every name, calm pictures instead of busy ones, and no timers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dutch and English.&lt;/strong&gt; Dutch is the default, English is one click away. The sound-alikes are different in each language, so each language has its own set of hooks, its own prompts and its own review.&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%2Fgb2137rjcum0ar68jvw9.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%2Fgb2137rjcum0ar68jvw9.png" alt=" " width="800" height="706"&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%2Fbasektdky70op0tqhwyg.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%2Fbasektdky70op0tqhwyg.png" alt=" " width="800" height="566"&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%2Fbhe09mqln5yp8n5u8j0l.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%2Fbhe09mqln5yp8n5u8j0l.png" alt=" " width="800" height="688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Who would pay for this
&lt;/h2&gt;

&lt;p&gt;The first user is my son, and he is the reason it exists. Parents of children with dyslexia, remedial teachers and tutors already pay for printed flash cards, apps and lessons, and they all have the same problem: a generic deck does not contain &lt;em&gt;this&lt;/em&gt; child's trick for remembering &lt;em&gt;this&lt;/em&gt; word.&lt;/p&gt;

&lt;p&gt;The thing worth paying for is not the Europe deck, it is the process that makes one: Gemma proposes sound-alike hooks for any list of names or words, finds calm, openly licensed pictures, and a parent or tutor only has to reject what is wrong. Topography is the first deck. Obvious next ones are other countries' topography, the periodic table, or a school's vocabulary list.&lt;/p&gt;

&lt;p&gt;The license modal for the app would be per owner (tutor, remedial teacher or school) based on the number of children they want to hand out access to. This way the cost per child kan be kept low and the hassle of parents having to buy a license is eliminated.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fimglink.cc%2Fcdn%2FY2d7qwQTzo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fimglink.cc%2Fcdn%2FY2d7qwQTzo.gif" alt="image" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this time I didn't manage to set-up hosting for the application so it only runs at a local machine.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/wvankuipers/topo-hooks" rel="noopener noreferrer"&gt;https://github.com/wvankuipers/topo-hooks&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works, and where Gemma fits
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;names (nl/en) ──► Gemma drafts sound-alike hooks, split into pieces
                  (each piece = one drawable object: "Bul" → bull)
              ──► Openverse search (CC0 / public domain / CC BY only)
              ──► Gemma looks at the pictures and rejects the bad ones
              ──► I review by exception ──► he picks his favorite per piece
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;1. Gemma drafts the hooks.&lt;/strong&gt; Gemma gets one name and has to return structured JSON: the pieces of the name, what each piece sounds like, and one drawable object per piece. The output is checked in code, not trusted: the pieces must spell the name exactly, and the drawing must be one to three English words that are not on a blocklist. If Gemma gets it wrong, the script tells it what was wrong and asks again. Dutch and English have separate system prompts and examples.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Pictures come from search, not from an image generator.&lt;/strong&gt; I first planned to generate the pictures. I chose to search Openverse instead, for three reasons: the pictures are real, human-made and openly licensed (CC0, public domain or CC BY, and every picture shows its credit and license), I do not have to worry about a generator producing something strange for a ten-year-old, and there is no GPU needed for generation. The cost is that some objects are hard to find as a clean drawing, which turned out to matter (see below).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Gemma checks the pictures.&lt;/strong&gt; The vision model sees each candidate and returns what it sees, a list of problems (wrong object, text in the picture, a checkerboard, a real person, not suitable for children, too busy, not calm) and a score. This is a filter, not a guarantee, so:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. A parent reviews by exception.&lt;/strong&gt; The review page shows every idea with its pictures, Gemma's notes and each picture's licence. Everything is approved unless I reject it. An idea only goes live when every piece has at least one approved picture. Then he picks his favorite picture per piece, stored in his own browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  What went wrong, and what I did about it
&lt;/h2&gt;

&lt;p&gt;This is the part I learned the most from.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A small model describes a cow and says everything is fine.&lt;/strong&gt; The 4B model accepted dogs and cows as "a bull". I made it write what it &lt;em&gt;sees&lt;/em&gt; first, then ask a second, text-only question: does what it saw match what I wanted? That removed most of these.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Transparent PNG" that is not.&lt;/strong&gt; Some stock images are JPEGs with a grey checkerboard painted in. A title that promises transparency over a file without an alpha channel is rejected before Gemma sees it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheap checks first.&lt;/strong&gt; On a CPU, one picture takes 15 to 25 seconds to look at. So the pipeline goes cheapest first: skip titles about matches, maps and logos, then a text-only title check (about 3 seconds), then the picture itself, then the text-only cross-check (about 1.5 seconds).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The quiz leaked the answer.&lt;/strong&gt; When only one of the three options had a picture, the picture gave away the right answer. Now pictures are only shown when all three options have one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rejected pictures were checked again on every run.&lt;/strong&gt; They are now remembered, so a re-run only looks at new candidates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Running Gemma: CPU, GPU and both
&lt;/h2&gt;

&lt;p&gt;I wanted this to work without a GPU, because that is what I have (a 2017 Intel MacBook Pro) and because a tutor or parent will not have one either.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Measured&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CPU only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gemma3:4b&lt;/code&gt; through Ollama&lt;/td&gt;
&lt;td&gt;about 12 to 15 s to draft a name, 15 to 25 s to look at a picture; about 3 to 6 minutes per piece&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gemma3:4b&lt;/code&gt; drafts on the laptop, &lt;code&gt;gemma3:12b&lt;/code&gt; checks pictures on a GPU Droplet&lt;/td&gt;
&lt;td&gt;TODO: fill in minutes per piece and total run time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The honest result of the CPU-only run: on a 4-core machine, &lt;strong&gt;2 of 7&lt;/strong&gt; pieces (Bulgaria, Norway, Denmark) ended up with a picture. The 4B model is strict and noisy, and under strict licenses some objects ("garage", "bull") have few clean drawings. So I built the hybrid route: the laptop drafts (cheap), and the big Gemma on a DigitalOcean GPU Droplet only looks at pictures, reached through an SSH tunnel so nothing is exposed to the internet. After the run the Droplet is switched off. Only public Openverse pictures are sent to it; nothing about my son leaves the laptop.&lt;/p&gt;

&lt;p&gt;TODO: after the Droplet run, add the real yield (pieces with at least one approved picture out of the total, per language) and one before/after picture of a hook that the 4B model could not find and the 12B model did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open innovation matters here
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cost.&lt;/strong&gt; The expensive part is looking at hundreds of pictures. With open weights I pay for a few hours of a Droplet, once per deck, and not per image to an API. That is what makes "a deck for every child" something a tutor could afford.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy.&lt;/strong&gt; The app has no accounts. His progress and his picture picks live in his own browser. Gemma runs on my laptop or on my own Droplet, never behind someone else's API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fit to a niche.&lt;/strong&gt; The hard problem is Dutch sound-alikes ("garije" sounds like "garage"). I could shape the prompts, examples and checks for exactly that, and swap the model without changing anything else (&lt;code&gt;--model&lt;/code&gt;, &lt;code&gt;--vision-model&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open all the way down.&lt;/strong&gt; Open-weight Gemma, Ollama, and pictures that creators chose to share under open licenses. Every picture in the app links back to its creator.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  First user
&lt;/h2&gt;

&lt;p&gt;I gave the app running on my local machine to my son. Together we set the images that he liked, that was a lot of fun as the results where not always very correct. Then he started clicking around on the map and learning all the pictures together with the names. He also did a practice run with the images enabled and already found that he remembered the names better then before by looking ayt the images. He didn't spend to much time on the app so I can't tell if it will help him to improve his knowledge about the countries and capitals in the long run but the first results looked promising. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;More decks: capital pictures, other regions, and a "bring your own list" mode for a tutor.&lt;/li&gt;
&lt;li&gt;A way to add my own picture for a piece when search comes up empty.&lt;/li&gt;
&lt;li&gt;Read the numbers: how many pictures a parent rejects after Gemma's check, per language.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Built with
&lt;/h2&gt;

&lt;p&gt;Next.js, TypeScript and Tailwind, &lt;code&gt;d3-geo&lt;/code&gt; for the map, Ollama with Gemma 3 (&lt;code&gt;gemma3:4b&lt;/code&gt;, &lt;code&gt;gemma3:12b&lt;/code&gt;), Openverse, &lt;code&gt;sharp&lt;/code&gt;, &lt;code&gt;vitest&lt;/code&gt;.&lt;/p&gt;

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