<?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: Kim Doyoung</title>
    <description>The latest articles on DEV Community by Kim Doyoung (@kimdooo).</description>
    <link>https://dev.to/kimdooo</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%2F4166531%2Fdccf2ca4-f6fb-442a-bbef-6752fbd607c3.jpg</url>
      <title>DEV Community: Kim Doyoung</title>
      <link>https://dev.to/kimdooo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kimdooo"/>
    <language>en</language>
    <item>
      <title>fieldcard: a pocket field card written offline by Gemma, so the screen is the shortest part of the walk</title>
      <dc:creator>Kim Doyoung</dc:creator>
      <pubDate>Tue, 06 Oct 2026 14:21:28 +0000</pubDate>
      <link>https://dev.to/kimdooo/fieldcard-a-pocket-field-card-written-offline-by-gemma-so-the-screen-is-the-shortest-part-of-the-3c5c</link>
      <guid>https://dev.to/kimdooo/fieldcard-a-pocket-field-card-written-offline-by-gemma-so-the-screen-is-the-shortest-part-of-the-3c5c</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;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;fieldcard&lt;/strong&gt; is a tiny command-line tool that writes you a paper field card for today's walk, and reads your walk back to you when you get home. Both halves run on an open-weight model on your own computer.&lt;/p&gt;

&lt;p&gt;Most "go outside" apps want you to keep looking at them: a map, a feed, a streak, a notification. I wanted the opposite. fieldcard is designed so you spend about thirty seconds with a screen before the walk and about ten after it, and the whole middle happens with a pencil.&lt;/p&gt;

&lt;p&gt;It has two commands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;quest&lt;/code&gt;&lt;/strong&gt; asks Gemma 4 E4B (running locally in Ollama) for five things to notice where you are going, in this season, before sunset. It prints them on an A6 card with checkboxes and a one-minute "sit spot". You print it, put it in your pocket, and close the laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;check&lt;/code&gt;&lt;/strong&gt; takes the photos you took, photos of your pencil sketches, or &lt;strong&gt;voice notes&lt;/strong&gt;, and the same local model decides which card item each one is evidence for. It writes a one-page field journal: what you found, what is "not yet", and one "next time" prompt per find, something to notice with your eyes, ears or nose rather than your phone.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The voice notes are my favourite part. Gemma 4 E4B takes audio as well as images, so on the trail you don't have to unlock your phone and frame a shot. You press record on any voice recorder and say "number four, seven little brown birds in the branches over the path", and that counts.&lt;/p&gt;

&lt;p&gt;It is for anyone who wants a reason to look closer on an ordinary walk: a lunch-break loop round the park, a weekend hike, a walk with a kid who needs a mission.&lt;/p&gt;

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

&lt;p&gt;87-second narrated walkthrough (real terminal output, real model output):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/kimdooo-a/fieldcard/blob/main/docs/fieldcard-demo.mp4" rel="noopener noreferrer"&gt;▶ fieldcard-demo.mp4&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The card that &lt;code&gt;quest&lt;/code&gt; wrote for an autumn walk in Seoul Forest park:&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%2Flp7c3c7pkb23u12271fp.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%2Flp7c3c7pkb23u12271fp.png" alt="A printable A6 field card titled Seoul Autumn Walk with five checkbox items and a sit spot" width="800" height="1252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the journal that &lt;code&gt;check&lt;/code&gt; wrote from the sample folder in the repo: six public-domain stand-in photos and two synthetic voice notes (I wanted a sample anyone can rerun, without shipping anyone's real walk):&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%2Fnxp7jytav6zszt64xz7f.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%2Fnxp7jytav6zszt64xz7f.png" alt="Field journal showing 4 of 5 items found, two voice notes transcribed, a lichen photo matched to the moss item, and the acorn item marked not yet" width="800" height="1146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It scored 4 out of 5. The acorn-caps item stays "not yet": the only acorn picture in the sample is two acorns on a twig against a blue backdrop, not caps on the ground, and the model said so. Bring the card back next time.&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/kimdooo-a" rel="noopener noreferrer"&gt;
        kimdooo-a
      &lt;/a&gt; / &lt;a href="https://github.com/kimdooo-a/fieldcard" rel="noopener noreferrer"&gt;
        fieldcard
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Offline field card + journal for a walk, written by a local open-weight model (Gemma via Ollama).
    &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;fieldcard&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A pocket field card, written by an open-weight model on your own computer. The screen is the shortest part of the walk.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fieldcard&lt;/code&gt; asks a small local model (Gemma 4 E4B, through &lt;a href="https://ollama.com" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt;) for five
things to notice outside today, where you are, in this season, before sunset. It prints them on an
A6 card. You go outside with the card and a pencil. When you come back, the same model looks at your
photos and listens to your voice notes, on the same machine, and writes a one-page field journal.&lt;/p&gt;
&lt;p&gt;No account. No API key. No cloud. Run it with &lt;code&gt;--airplane&lt;/code&gt; and the process refuses every network
connection that isn't to this computer.&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The card (print it)&lt;/th&gt;
&lt;th&gt;The journal (after the walk)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/kimdooo-a/fieldcard/docs/card.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fkimdooo-a%2Ffieldcard%2FHEAD%2Fdocs%2Fcard.png" alt="card"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/kimdooo-a/fieldcard/docs/journal.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fkimdooo-a%2Ffieldcard%2FHEAD%2Fdocs%2Fjournal.png" alt="journal"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Demo video (87 s, narrated): &lt;a href="https://github.com/kimdooo-a/fieldcard/docs/fieldcard-demo.mp4" rel="noopener noreferrer"&gt;docs/fieldcard-demo.mp4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05" rel="nofollow"&gt;DEV × Hacktoberfest 2026 Open-Source AI Challenge, Week 1: Touch Grass&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick start&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;You need…&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/kimdooo-a/fieldcard" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;One Python file, standard library only (Pillow and ffmpeg are optional), MIT licensed, twelve offline unit tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma4:e4b
python fieldcard.py &lt;span class="nt"&gt;--airplane&lt;/span&gt; quest &lt;span class="nt"&gt;--place&lt;/span&gt; &lt;span class="s2"&gt;"Seoul Forest park, Seoul"&lt;/span&gt; &lt;span class="nt"&gt;--habitat&lt;/span&gt; park &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--minutes&lt;/span&gt; 40 &lt;span class="nt"&gt;--lat&lt;/span&gt; 37.544 &lt;span class="nt"&gt;--lon&lt;/span&gt; 127.037 &lt;span class="nt"&gt;--tz&lt;/span&gt; 9
python fieldcard.py &lt;span class="nt"&gt;--airplane&lt;/span&gt; check examples/2026-10-06-seoul-autumn-walk.json samples/walk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;The model.&lt;/strong&gt; Everything runs through &lt;a href="https://ai.google.dev/gemma" rel="noopener noreferrer"&gt;Gemma 4 E4B&lt;/a&gt; in Ollama: a 7.5B-parameter open-weight model, 6.6 GB on disk, that takes text, images and audio. One model does all three jobs: writing the card, looking at photos, and transcribing voice notes. On my desktop GPU, once the model is loaded, a card takes 1 to 3 seconds per attempt, and the sample journal (six photos and two voice notes) took about 10 seconds in total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code decides the facts, the model writes the words.&lt;/strong&gt; I didn't want the model guessing things that can be computed. The season comes from the date and the hemisphere. Sunrise and sunset come from the NOAA solar position equations, about 40 lines of math, so there is no weather API to call. (For central Seoul on 6 October it gives 18:10; api.sunrise-sunset.org says 18:10:20.) The model gets those facts in the prompt and only writes the five observations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code checks every card before it can be printed.&lt;/strong&gt; Gemma returns the card as JSON constrained by a schema, and then plain Python checks it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exactly five items, short enough to read at a glance, no duplicates;&lt;/li&gt;
&lt;li&gt;nothing to eat, taste, forage, touch, feed, climb, catch or leave the trail for;&lt;/li&gt;
&lt;li&gt;no phones, screens or apps on the card;&lt;/li&gt;
&lt;li&gt;if an item says "count it", it has to say what to count.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a rule is broken, the card goes back to the model with the list of problems and gets rewritten. The last rule exists because of something I kept seeing: Gemma liked to mark "the crunch of dry leaves underfoot" as a tally item. You can't count a sound. Writing that rule into the system prompt didn't change much. Checking it in code did, and the rewrite takes about a second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;attempt 1: 2.75s, 347 tokens -&amp;gt; rejected: item 3 is a tally but does not say what to count
attempt 2: 1.35s, 275 tokens -&amp;gt; ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the pattern I'd recommend to anyone building on a small local model. Let it do the creative part, and let a few lines of boring code hold the line on the parts that have to be right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reading the walk back.&lt;/strong&gt; Each photo or voice note goes to the same model along with the card, and it answers with a small JSON verdict: what it sees (or the transcript), which item it matches, how confident it is, and one thing to look closer at next time. Each card item keeps its most convincing piece of evidence. Everything else becomes a "bonus find", because a dandelion seed head you stopped to look at still counts as a good walk.&lt;/p&gt;

&lt;p&gt;My first version was too generous. It credited a &lt;strong&gt;bracket fungus&lt;/strong&gt; for "moss on rough bark", and its own explanation even admitted the fungus wasn't moss. One extra sentence in the prompt fixed it: &lt;em&gt;a match means the thing the item names is itself visible; something nearby or similar is not a match.&lt;/em&gt; Now the fungus is a bonus find, and when it accepts lichen for "moss" it says so in the journal and suggests checking next time whether the growth is soft and green or hard and crusty. I'd rather it be honest about being close than pretend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Airplane mode.&lt;/strong&gt; &lt;code&gt;--airplane&lt;/code&gt; swaps out Python's socket functions for the process, so any lookup or connection to a host that isn't loopback raises an error. One unit test checks that &lt;code&gt;https://example.com&lt;/code&gt; is refused while &lt;code&gt;127.0.0.1&lt;/code&gt; still works. It's a cheap way to &lt;em&gt;prove&lt;/em&gt; the privacy claim instead of just making it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The output is paper-shaped.&lt;/strong&gt; The card and the journal are static HTML with print CSS (A6 for the card) and no JavaScript, with the journal's thumbnails inlined. They open offline, they print, and nothing on them pulls you back in.&lt;/p&gt;

&lt;p&gt;I built it with an AI coding agent (Claude Code) as a pair programmer. The model inside the product, the thing that makes it work, is Gemma running locally.&lt;/p&gt;

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

&lt;p&gt;Because of where this tool is used, and what it is shown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It works where the signal doesn't.&lt;/strong&gt; The card is generated before you leave and printed, and the journal is written after you're back. Neither needs the internet, so a trailhead with no reception doesn't matter. With open weights the model is a file on my disk. It won't be deprecated next quarter, rate-limited, or put behind a login.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your walk stays yours.&lt;/strong&gt; Photos from a walk are surprisingly personal: your street, your kid's hand holding an acorn, the route you take every evening. Voice notes are your actual voice. A closed vision API would mean uploading all of that to a server I don't control just to get a checkbox ticked. Here the model runs on the same machine as the photos, and &lt;code&gt;--airplane&lt;/code&gt; makes that something you can check rather than trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It costs nothing to run, so it can be generous.&lt;/strong&gt; There's no per-call bill, so retrying a card that breaks a rule is free, and so is reading every photo from a long walk. That changes design decisions: I could add the "check in code, rewrite if wrong" loop without counting tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I can see and change everything.&lt;/strong&gt; The prompts, the rules and the model are all swappable. &lt;code&gt;--model&lt;/code&gt; takes any Ollama model with vision, and a club could fine-tune a small model on its local flora and get cards that name the right maples. With a closed API, how the model behaves is someone else's decision. Here it's a few lines in a file anyone can fork.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open-weight multimodal is the enabling piece.&lt;/strong&gt; Until recently, "local" meant text-only, or a separate speech model, a separate vision model, and a lot of glue. A single open 7.5B model that reads images &lt;em&gt;and&lt;/em&gt; hears voice notes is what made the "keep your phone in your pocket and just talk" design practical on a laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;I didn't record a DevRelay session for this one. The commit history and the tests in the repo show how it came together.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 4 E4B, run locally through Ollama, is the whole engine: it writes the cards, reads the photos, and transcribes the voice notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs&lt;/strong&gt;: the demo's narration was generated with ElevenLabs text-to-speech, and so were the two sample voice notes in the repo, so the samples ship nobody's real voice. ElevenLabs is not used at runtime; fieldcard itself stays offline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Credits: sample photos are public domain / CC0 from Wikimedia Commons (full list in &lt;code&gt;samples/walk/CREDITS.md&lt;/code&gt;). Sunrise and sunset use the NOAA General Solar Position equations.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Now go outside.&lt;/p&gt;

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