<?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: Dev3 ^~^</title>
    <description>The latest articles on DEV Community by Dev3 ^~^ (@shashank03dev).</description>
    <link>https://dev.to/shashank03dev</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%2F4164403%2F567fe475-6428-41a8-9187-cb026fd21371.png</url>
      <title>DEV Community: Dev3 ^~^</title>
      <link>https://dev.to/shashank03dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shashank03dev"/>
    <language>en</language>
    <item>
      <title>Paper Trail: Gemma writes my neighbourhood into a paper zine, so my phone can stay home</title>
      <dc:creator>Dev3 ^~^</dc:creator>
      <pubDate>Sun, 11 Oct 2026 02:37:56 +0000</pubDate>
      <link>https://dev.to/shashank03dev/paper-trail-gemma-writes-my-neighbourhood-into-a-paper-zine-so-my-phone-can-stay-home-3jhj</link>
      <guid>https://dev.to/shashank03dev/paper-trail-gemma-writes-my-neighbourhood-into-a-paper-zine-so-my-phone-can-stay-home-3jhj</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;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr0c3nha50uubhilovqqy.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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr0c3nha50uubhilovqqy.gif" alt="A blank sheet gets printed into an 8-page zine, folds into a booklet, and the phone slides off the desk under a red stamp that says PHONE STAYS HOME" width="760" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most "go outside" apps want to come with you. They want a photo of the tree to prove you saw it, a check-in at the bench, a notification after ten minutes outside. You end up in the park looking at the same screen you left on your desk.&lt;/p&gt;

&lt;p&gt;I wanted an app you use for two minutes before a walk and two minutes after, and that stays at home in between.&lt;/p&gt;

&lt;p&gt;When I was thinking about it I kept coming back to the old I-Spy spotter books. Kids carried a little paper book, ticked off what they saw, and posted the finished book back for a reward. No battery, no signal, and the fun part happened outside.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Paper Trail&lt;/strong&gt;: an open model writes your real neighbourhood into an 8-page paper zine, you walk with paper and a pencil, and when you're back it reads your pencil marks from one photo.&lt;/p&gt;

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

&lt;p&gt;You pick a starting point, how long you want to walk and who's coming. Paper Trail pulls the real streets and places around you from OpenStreetMap, plans a loop, and Gemma writes the zine. It prints on one sheet of US Letter or A4. One cut, a few folds, and it's a pocket booklet.&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%2F0e6yi3jx2ffkhaa91i9g.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%2F0e6yi3jx2ffkhaa91i9g.jpg" alt="The setup page: pick a start point, a length and who's coming, then Fold my trail" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The eight pages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cover&lt;/strong&gt;: a title written for your spot, today's weather and a PHONE STAYS HOME stamp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map&lt;/strong&gt;: your real streets, hand-drawn in code, with the loop and numbered stops&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route&lt;/strong&gt;: directions by landmark, not turn-by-turn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I-Spy&lt;/strong&gt;: 8 to 10 things to tick off, at least six of them real mapped places on your route&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Listen&lt;/strong&gt;: stand still for a minute and write down the loudest, the quietest and a new sound&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Riddle&lt;/strong&gt;: the answer is a real stop on your route&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sketch&lt;/strong&gt;: a drawing prompt tied to something you'll pass&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bring me home&lt;/strong&gt;: a QR code and a note on which model wrote it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the zine Gemma wrote tonight for Dolores Park in San Francisco, exactly as it prints. The top row is upside down on purpose: that's how a one-sheet zine folds.&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%2Fk585suqn55llb9w416wl.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%2Fk585suqn55llb9w416wl.jpg" alt="The printed sheet: Listen, I-Spy, The Route and The Map upside down on the top row; The Riddle, Sketch, Bring me home and the cover " width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you leave. When you come back, you unfold the sheet and take one photo. The browser finds the four black corner squares, flattens the photo and checks every box for pencil. Gemma reads your handwriting, judges your riddle answer and describes your sketch. You get a Field Report with a postmark, a Trail Book of every walk, and a counter of time on the screen against time outside.&lt;/p&gt;

&lt;p&gt;It's for anyone who wants a reason to go outside without bringing the feed along: kids, families, someone walking a dog, or a teacher who wants thirty different trails starting at the school gate.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a href="https://paper-trail-t6mp.onrender.com" rel="noopener noreferrer"&gt;paper-trail-t6mp.onrender.com&lt;/a&gt;. No sign-up. If you'd rather not share your location, pick one of the example parks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;15-second launch video:&lt;/strong&gt; &lt;a href="https://github.com/shashank03-dev/Paper-Trail-WEEK-1/blob/main/video/out/paper-trail-launch-1920x1080.mp4" rel="noopener noreferrer"&gt;watch it here&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%2Fnmdz090b1ey3pf7iyvj8.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%2Fnmdz090b1ey3pf7iyvj8.jpg" alt="The landing page: " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While Gemma writes, the sheet prints itself page by page, so you're not staring at a spinner:&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%2Ftka3ix0gdpxc6vqrec7j.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%2Ftka3ix0gdpxc6vqrec7j.jpg" alt="Writing your trail: Gemma is planning the trail, a page counter shows 1 of 8, and the map page has already printed" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The read-back, on the live site
&lt;/h3&gt;

&lt;p&gt;To test the return trip end to end, I took that Dolores Park sheet and added the kind of marks you'd make on a walk: six ticks out of nine, three sounds, the riddle answer and a quick tree. I drew the marks in code and tilted and shadowed the sheet like a phone photo. That's the photo I uploaded:&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%2Fw40h2135c8komeev2w1d.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%2Fw40h2135c8komeev2w1d.jpg" alt="A tilted photo of the sheet on a wooden table, with pencil ticks, handwriting and a small tree sketch" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The browser found the corners on its own:&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%2Fkqpu4s7l263ez776b186.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%2Fkqpu4s7l263ez776b186.jpg" alt="Welcome back: Corners found, with the four markers outlined on the photo and a Read my sheet button" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;28 seconds later, the Field Report:&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%2Feare4n6kleho8kuzsyht.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%2Feare4n6kleho8kuzsyht.jpg" alt="The Field Report for Golden Hydrants and Hilltop Views: a red Trail Office stamp for 110 points, spotted 6 of 9, the I-Spy list, and a note from the Trail Office" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It got all six ticks and all three untouched boxes right. It read "golden fire hydrant" and marked the riddle &lt;strong&gt;Right!&lt;/strong&gt;. It described the sketch as "a simple tree with a straight trunk and two overlapping circles for its leafy canopy", which is exactly what I drew. The Trail Office note even quoted my sounds back to me.&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/shashank03-dev" rel="noopener noreferrer"&gt;
        shashank03-dev
      &lt;/a&gt; / &lt;a href="https://github.com/shashank03-dev/Paper-Trail-WEEK-1" rel="noopener noreferrer"&gt;
        Paper-Trail-WEEK-1
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Every touch-grass app wants to come with you. This one stays home.
    &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;Paper Trail&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Every touch-grass app wants to come with you. This one stays home.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Paper Trail turns your real neighbourhood into a one-sheet, 8-page paper zine written by an open model (Gemma 4 — on your own machine, or through Ollama Cloud or the Gemini API), so your phone can stay home. When you get back, Gemma reads your pencil ticks, handwriting and sketches from one photo and stamps your Trail Book.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/shashank03-dev/Paper-Trail-WEEK-1/docs/media/hero.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fshashank03-dev%2FPaper-Trail-WEEK-1%2FHEAD%2Fdocs%2Fmedia%2Fhero.gif" alt="TODO: hero GIF — the sheet printing panel by panel, then folding"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An entry for the DEV Hacktoberfest Open-Source AI Challenge, Week 1: &lt;strong&gt;Touch Grass&lt;/strong&gt;. MIT licensed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt; Setup ─► OpenStreetMap features + streets ─► route.ts picks a 5–6 stop loop (no AI)
   │      Open-Meteo weather                         │
   │                                                 ▼
   │                       Gemma writer: plan  ─►  cover · route · listen · back
   │                       (JSON schema → zod → grounding check → retry → template)
   ▼                                                 │
 IndexedDB  ◄──── the trail ◄──── one SVG sheet, mm-exact, drawn with rough.js&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/shashank03-dev/Paper-Trail-WEEK-1" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;MIT licensed. Next.js 15, TypeScript, Tailwind 4, rough.js for every wobbly line, three.js for the folding sheet on the landing page, and Dexie for storage in the browser. There are 90 unit tests and 12 browser tests that run the whole flow, setup to stamp, against a mock model.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; setup ─► OpenStreetMap places + streets ─► planner picks a 5–6 stop loop (no AI)
                                                  │
                         Gemma: plan ─► cover · route · listen · back
                         (JSON schema → zod → grounding check → retry → template)
                                                  │
                         one SVG sheet in millimetres ─► print, cut, fold, walk
                                                  │
 Field Report ◄─ combine ◄─ Gemma reads 4 page crops ◄─ warp ◄─ corner markers ◄─ one photo
                    ▲
                    └── ink count inside every printed checkbox (no AI)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Gemma writes, but it doesn't navigate
&lt;/h3&gt;

&lt;p&gt;A plain planner picks the loop: the most interesting nearby places that fit your time at walking pace. Gemma gets that route plus a short list of real places, each with an id, and writes in two steps: first a plan (title, I-Spy list, riddle, sketch prompt), then the text for each page.&lt;/p&gt;

&lt;p&gt;Every answer goes through a JSON schema, then zod, then checks that the model can't talk its way past. Every place has to exist. At least six I-Spy items have to be real places. The riddle's answer has to be a stop on the route. Here's the riddle check, trimmed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;riddleProblems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Riddle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WriterContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waypoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;featureId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;wp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;targetFeatureId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`riddle targetFeatureId "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;targetFeatureId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" must be one of the WAYPOINT ids: ...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nf"&gt;idLeaks&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;fitsEverywhere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;riddleText&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;riddle text is too long for its panel; make it shorter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;p&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;The text also has to fit the page, and a page is 67 mm wide. SVG doesn't wrap text, so I generated width tables from the actual font files and measure every line before it's accepted. If any check fails, Gemma gets the exact problem back and another try. If it keeps failing, a template built from the same real places takes over, so you always get a zine.&lt;/p&gt;

&lt;p&gt;The checks earned their place tonight. Writing the Dolores Park zine took 5 calls, and 2 of them (the route page and the listen page) failed a check the first time and passed on the retry:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Live run, Dolores Park, 20 minutes&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Gemma 4 26B A4B, on the Gemini API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to write the whole zine&lt;/td&gt;
&lt;td&gt;41.5 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed&lt;/td&gt;
&lt;td&gt;13.8 tokens/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Calls that needed a retry&lt;/td&gt;
&lt;td&gt;2 of 5 (route, listen)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I-Spy items tied to real mapped places&lt;/td&gt;
&lt;td&gt;7 of 9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to read the photo back&lt;/td&gt;
&lt;td&gt;28.5 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ticks read correctly&lt;/td&gt;
&lt;td&gt;9 of 9 boxes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The paper is one SVG, in millimetres
&lt;/h3&gt;

&lt;p&gt;Everything on the sheet is drawn in code: the map, the weather icon, the stamp, the QR code. There are no stock images and no generated images. One layout file holds every measurement, so the preview on screen is the print, and the reader knows exactly where every checkbox was printed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading is where the open model earns its keep
&lt;/h3&gt;

&lt;p&gt;One photo of a whole sheet is a lot to ask of a model, so the browser does the geometry first, in plain TypeScript with no OpenCV: find the four corner markers, solve a homography, warp the photo flat and cut out the pages. Gemma then gets four small crops (I-Spy, Listen, Riddle, Sketch), each with a prompt saying what should be on it.&lt;/p&gt;

&lt;p&gt;For the ticks there's a second opinion that needs no model at all. Because I know where every box was printed, I measure how much pencil is inside it. When Gemma and the ink count agree, the answer stands. When they don't, the report marks the row in amber and you tap to fix it.&lt;/p&gt;

&lt;p&gt;On 30 synthetic phone photos (tilted, rotated, shadowed, blurred), the corner markers were found every time, and the ink count alone got every tick right. Real crumpled paper in bad light is harder than that, which is why every result can be fixed with one tap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bugs I hit along the way
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;My first sheet didn't fold right.&lt;/strong&gt; I'd offset the page grid by the print margin, so the real folds landed 3 mm off the page edges. Pages are now exact quarters of the sheet, with the margins inside each page. There's a &lt;code&gt;/fold-test&lt;/code&gt; page with giant page numbers to check a printer before printing the real thing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma on the Gemini API thinks before it answers.&lt;/strong&gt; It spent its token budget on a hidden thought block and sometimes ran out before the JSON. Requests now ask for minimal reasoning, and any thought text that still arrives is stripped before parsing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real output leaked internal ids.&lt;/strong&gt; The prompt gives places ids like &lt;code&gt;n9000024&lt;/code&gt;, and Gemma sometimes printed them in the text ("the fountain n9000024"). Now every piece of text is checked for ids and sent back for a retry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The same API takes no system prompt and no JSON schema for Gemma.&lt;/strong&gt; The client folds the system prompt into the request and puts the schema in the prompt, and the zod checks do the rest.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running it
&lt;/h3&gt;

&lt;p&gt;Locally it runs on Ollama with &lt;code&gt;gemma4:e4b&lt;/code&gt;, or &lt;code&gt;gemma4:e2b&lt;/code&gt; on smaller machines. The live site runs on Render as one Docker web service in Singapore, and it uses Gemma 4 26B A4B through the Gemini API, with Gemma on Ollama Cloud as the fallback. Every default in the config is a Gemma model. Render redeploys on every push to &lt;code&gt;main&lt;/code&gt;. My last change went live 2 minutes 48 seconds after the push.&lt;/p&gt;

&lt;p&gt;Since the site is public, it has guard rails: a daily cap on model calls, at most four model calls at once with a queue, a health check that's cached so the model badge doesn't spend quota, and a block on other sites calling the API.&lt;/p&gt;

&lt;p&gt;Even the launch video is open source end to end. It's drawn in code from the same zine renderer and the landing page's fold model, and the voice is Kokoro-82M, an Apache-2.0 text-to-speech model that runs on a CPU.&lt;/p&gt;

&lt;p&gt;I built Paper Trail with an AI coding agent doing a lot of the typing while I made the calls.&lt;/p&gt;

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

&lt;p&gt;Paper Trail works with two things I don't want to hand out lightly: a map of where I live, and a photo of my handwriting.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It can run entirely on my own machine.&lt;/strong&gt; With Gemma in Ollama next to the app, my location and my notes never leave my computer. There are no accounts and no analytics, and trails live in the browser. On the hosted site, the zine prints which provider wrote it, and the "your location never left this machine" line only appears when that's true.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I can swap the model with one environment variable.&lt;/strong&gt; E2B for a small machine, E4B on a laptop, 26B or 31B hosted. Same prompts, same checks, same zine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There's always a way to run it yourself.&lt;/strong&gt; If a hosted provider rate-limits, the next one serves the same open model. If you don't trust any provider, you run it on your own hardware. A school, a library or a scout group can run it for free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could make a small model reliable instead of hoping a big one is.&lt;/strong&gt; Because I control the whole loop, I could split the photo into four focused crops, add an ink count as a second opinion, and check every answer against the real map. A closed API would give me one big answer back and my fingers crossed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Where closed is better: a big hosted model writes a little more colourful copy, and it's faster than a small model on a CPU. For an app you open for two minutes before a walk, that's a fine trade.&lt;/p&gt;

&lt;p&gt;And once it's printed, it works with no signal and no battery. It's paper.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it can't do (yet)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OpenStreetMap coverage varies. Where a place is thinly mapped, the I-Spy list leans on things like "a passing dog" or "a leaf that's two colours".&lt;/li&gt;
&lt;li&gt;Hard light hurts. A shadow across the checkboxes can fool both the ink count and the model, which is why every row is one tap to fix.&lt;/li&gt;
&lt;li&gt;Small models still misread handwriting sometimes. The report never hides that.&lt;/li&gt;
&lt;li&gt;The screen-versus-outside counter only times the outside part if you print from the app.&lt;/li&gt;
&lt;/ul&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 does both halves of the app. It writes every zine (grounded in real places, schema-checked, fitted to the page) and reads every sheet that comes back (handwriting, riddle answers and sketch descriptions from four focused crops). It runs as E2B or E4B on Ollama locally, and as 26B A4B on the Gemini API with 31B on Ollama Cloud as the fallback for the live site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render:&lt;/strong&gt; the live site is a Docker web service on Render that redeploys on every push to &lt;code&gt;main&lt;/code&gt;, with a daily model budget, a concurrency gate and a cached health check so a public demo can't run up the bill. The repo also includes a Render Blueprint that runs Gemma itself in an Ollama private service with a persistent model disk, next to the web app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built solo.&lt;/p&gt;

&lt;p&gt;Print one this weekend, leave your phone at home, and tell me in the comments what you spotted.&lt;/p&gt;

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