<?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: Rupam Das</title>
    <description>The latest articles on DEV Community by Rupam Das (@rudas).</description>
    <link>https://dev.to/rudas</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%2F4152296%2Fd6eb447f-fce9-4d87-9f3b-c59745bf4fd7.png</url>
      <title>DEV Community: Rupam Das</title>
      <link>https://dev.to/rudas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rudas"/>
    <language>en</language>
    <item>
      <title>I was too lazy to build an app for every chore. So I built JackApp.</title>
      <dc:creator>Rupam Das</dc:creator>
      <pubDate>Mon, 26 Oct 2026 06:50:00 +0000</pubDate>
      <link>https://dev.to/rudas/i-was-too-lazy-to-build-an-app-for-every-chore-so-i-built-jackapp-2e09</link>
      <guid>https://dev.to/rudas/i-was-too-lazy-to-build-an-app-for-every-chore-so-i-built-jackapp-2e09</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I typed “remind me to drink water” and got a reminder with a Done button. It nudges me three times a day; tapping Done adds to a check-in count.&lt;/p&gt;

&lt;p&gt;I also asked for an expense tracker: an amount, a note and a running total. Those are small chores. I wanted the effort of making a tool to match their size.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;JackApp: an Android app that turns a plain-language description into a small tool you can preview, save and use again.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Jack of all trades, master of your chores.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Gemma 4 E2B runs on the phone through LiteRT-LM. After a one-time download, you can build supported tools offline. JackApp uses seven pre-built recipe families: lists and logs, counters, calculators, timers, reminders, bill splitters and AI helpers for text or photos.&lt;/p&gt;

&lt;p&gt;I didn't have to build a screen, wire a button or set up storage for my reminder or expense log. Each tool keeps its own data, ready for the next time I need it.&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%2Fnbwfku2xc42s1ijetrce.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%2Fnbwfku2xc42s1ijetrce.gif" alt="Illustrated water counter and receipt-enabled expense log" width="760" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Two examples of small personal tools. This illustration uses browser captures and example data.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;The video walks through describing, previewing and using a tool. It uses phone-sized browser captures and example data; the Android generation measurements are in the evaluation section below.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/2HQtCgSd0Vk" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To try on-device generation, you'll need an Android development build and the roughly &lt;strong&gt;2.6 GB&lt;/strong&gt; model download. &lt;a href="https://github.com/ru-das/jackapp#run" rel="noopener noreferrer"&gt;The repository has setup instructions&lt;/a&gt;. There isn't a ready-to-install Android download linked here yet.&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/ru-das" rel="noopener noreferrer"&gt;
        ru-das
      &lt;/a&gt; / &lt;a href="https://github.com/ru-das/jackapp" rel="noopener noreferrer"&gt;
        jackapp
      &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;JackApp&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Jack of all trades, master of your chores. Describe a tiny tool in plain language and get a working mini-app, run on your own device with an open model (Gemma 4).&lt;/p&gt;
&lt;p&gt;Built for the DEV Hacktoberfest Open-Source AI Challenge, Week 3. Web app: &lt;a href="https://ru-das.github.io/jackapp/" rel="nofollow noopener noreferrer"&gt;https://ru-das.github.io/jackapp/&lt;/a&gt;. Android: build it yourself (below).&lt;/p&gt;
&lt;p&gt;The AI never writes code. It picks and fills in pre-built pieces, and the result is a JSON spec that the app validates and renders. Mini-apps and their data stay on the device. No accounts, no telemetry.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create:&lt;/strong&gt; type what you need ("track which plants I watered and remind me every Sunday"). You get a preview, then save it. A request the catalog cannot build gets a plain "can't build that yet" card instead of a broken app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ready-made starters:&lt;/strong&gt; a checklist and a tip calculator on Home, no AI needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mini-apps can use the model&lt;/strong&gt;…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ru-das/jackapp" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Expo, React Native and TypeScript provide the app. Zod and semantic checks validate the generated descriptions. They are data, never executable code.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Three minutes, no usable tool
&lt;/h3&gt;

&lt;p&gt;My first approach asked Gemma to describe the entire mini-app: layout, fields, stored values, actions and every connection between them. JackApp checked the result, asked for repairs, then checked again.&lt;/p&gt;

&lt;p&gt;On my Moto G45, one attempt spent &lt;strong&gt;175 seconds&lt;/strong&gt; producing a description that failed validation. The repair then incorrectly refused the request. More than three minutes had passed, and I still had no usable tool.&lt;/p&gt;

&lt;p&gt;Three minutes is a long time to wait for help remembering when you watered a plant.&lt;/p&gt;

&lt;p&gt;I shortened the output, moved from full JSON to compact text, and asked repairs to return only the parts that needed changing. A follow-up phone test produced structurally valid fridge and plant tools, but the bill splitter and expense log still failed. Repairs made the results worse.&lt;/p&gt;

&lt;p&gt;The model was repeatedly being asked to rediscover things I already knew: a counter's Add button increases a number; a log remembers its entries. I could put those connections in code once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gemma fills in. Recipes build.
&lt;/h3&gt;

&lt;p&gt;I introduced &lt;strong&gt;recipes: pre-built app patterns with settings the model fills in.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For an expense log, Gemma supplies the fields and the total you want. The recipe supplies the form, Add button, saved rows and their connections.&lt;/p&gt;

&lt;p&gt;The request &lt;strong&gt;“Log expenses with a name and amount and show the total spent”&lt;/strong&gt; produced these settings, excerpted from the recorded phone run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name Expense Log
description Log what you spend and see the total
noun expense
field name text Expense
field amount money Amount
total sum amount Total spent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gemma asks for a name, an amount and a sum. Code builds the tool and calculates the total as entries change.&lt;/p&gt;

&lt;p&gt;The recipe path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
  → check known capability limits
  → choose a recipe (ask Gemma to classify when needed)
  → Gemma fills in settings
  → code builds the description
  → validate → preview → save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If no recipe fits, or its settings are unusable, JackApp can try free-form generation with the same local model. That path is slower and less reliable. Descriptions that fail validation don't reach the preview or get saved.&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%2Fh4oxfvg2w8edpnlwkt2j.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%2Fh4oxfvg2w8edpnlwkt2j.gif" alt="Examples from JackApp's seven recipe families" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Illustrated counter, timer and plant tools. Gemma supplies settings; recipes supply their structure.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What the generated expense log actually does
&lt;/h3&gt;

&lt;p&gt;In a follow-up &lt;strong&gt;desktop replay&lt;/strong&gt;, I took the expense description from the phone log and executed it through the current action engine:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&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;Add Lunch 12.50 and Bus 7.50&lt;/td&gt;
&lt;td&gt;Total: &lt;strong&gt;20.00&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Change Bus to 8.50&lt;/td&gt;
&lt;td&gt;Total: &lt;strong&gt;21.00&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete Bus&lt;/td&gt;
&lt;td&gt;Total: &lt;strong&gt;12.50&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Adding an entry also cleared the input fields. In the saved bill-splitter description, entering a bill of 120, a tip of 12 and four people produced &lt;strong&gt;33 each&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These checks exercised actions and calculations with supplied values. They didn't exercise phone screens, persistent storage, notifications or receipt recognition.&lt;/p&gt;

&lt;p&gt;Separately, manual phone checks covered offline building, saved data surviving reopening, grocery and receipt photo reading, and a reminder notification. Those were individual checks, not an accuracy study.&lt;/p&gt;

&lt;h3&gt;
  
  
  A saved tool can use Gemma again
&lt;/h3&gt;

&lt;p&gt;Ordinary entries and calculations use app logic. A photo-enabled tool calls Gemma when you ask it to read a new image.&lt;/p&gt;

&lt;p&gt;For receipt reading, I made a separate request that mentioned receipts; that feature wasn't part of the basic expense-log request above. JackApp cannot yet edit an existing tool through conversation.&lt;/p&gt;

&lt;p&gt;The fridge tool has a similar division of work: Gemma lists visible food, you correct that list, and code compares it with the usual items you keep. You check the resulting shopping list before buying; a photo can't establish that something hidden at the back is missing.&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%2F7s52fkbq8fmwld8u6kxp.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%2F7s52fkbq8fmwld8u6kxp.gif" alt="Illustrated fridge-photo review and shopping-list workflow" width="720" height="405"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Illustrative workflow with a generated sample photo and example results, not an OCR or vision-accuracy test.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What happened on the phone
&lt;/h3&gt;

&lt;p&gt;On a &lt;strong&gt;Moto G45, Android 15, 8 GB RAM&lt;/strong&gt;, the recipe run produced:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;Recorded result&lt;/th&gt;
&lt;th&gt;Build time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fridge-photo shopping list&lt;/td&gt;
&lt;td&gt;Structurally valid&lt;/td&gt;
&lt;td&gt;15.6 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plant watering tracker&lt;/td&gt;
&lt;td&gt;Structurally valid; missed the requested tap behavior&lt;/td&gt;
&lt;td&gt;18.5 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Receipt-based bill splitter&lt;/td&gt;
&lt;td&gt;Structurally valid&lt;/td&gt;
&lt;td&gt;5.7 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expense log&lt;/td&gt;
&lt;td&gt;Structurally valid&lt;/td&gt;
&lt;td&gt;12.1 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactive physics simulation&lt;/td&gt;
&lt;td&gt;Rejected by the capability gate in code&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatically fetched live weather&lt;/td&gt;
&lt;td&gt;Rejected by the capability gate in code&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The four descriptions passed without repair, with a &lt;strong&gt;13.9-second median build time while the model was loaded&lt;/strong&gt;, using GPU inference and speculative decoding. An earlier run measured &lt;strong&gt;21.8 seconds to load the model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ru-das/jackapp/blob/main/evals/results/eval-2026-10-25T10-45-22-748Z-gemma-4-E2B-it.litertlm-noplan.json" rel="noopener noreferrer"&gt;The six-request log contains the prompts, settings and generated descriptions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These times exclude model download, loading and later photo reads. Four structurally valid descriptions don't establish four correctly completed chores, and this wasn't a controlled speed comparison with the early 175-second failure.&lt;/p&gt;

&lt;p&gt;The plant tracker shows why. I asked to tap a plant to record watering. Its generated tap action opened a name editor instead, leaving the watering date unchanged in the desktop replay. A List and an update action somewhere in the description satisfied the evaluator's presence checks. &lt;strong&gt;The preview still needs to be checked against the actual request.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;A receipt reveals spending. A reminder reveals a routine. A fridge photo was taken inside someone's home.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open weights let Gemma run on the phone.&lt;/strong&gt; In on-device mode, supported builds and photo reads don't send those inputs to a hosted inference service. There is no cloud fallback or per-request inference bill.&lt;/p&gt;

&lt;p&gt;The tradeoff is the 2.6 GB download, plus memory, battery and waiting time. Local inference made long outputs expensive in a way I could feel: I was sitting there waiting. Recipes gave Gemma less to write, while keeping ordinary buttons, calculations and stored data independent of another generation request.&lt;/p&gt;

&lt;p&gt;The source is open too. Someone can inspect how a description becomes a tool, change a recipe or add one. Its future doesn't have to depend on my finding time to maintain it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&lt;/h2&gt;

&lt;p&gt;I'm using JackApp for the little things I built it for.&lt;/p&gt;

&lt;p&gt;My water reminder nudges me three times a day. I tap Done, and it adds to my check-in count. For expenses, I open my saved log, enter an amount and a note, and the total updates. With the receipt-enabled version, I photograph the receipt, check what it read, and save the expense.&lt;/p&gt;

&lt;p&gt;I don't have to describe those tools again every time I need them. The reminder keeps its count, the log keeps its entries, and both are there when I come back.&lt;/p&gt;

&lt;p&gt;That's the laziness I wanted: describe a chore once, make a tool for it, and keep using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits and Next Steps
&lt;/h2&gt;

&lt;p&gt;There are practical limits. You provide the inputs: JackApp can't watch your bank account, read other apps or fetch live weather. Photo results need review. Reminders need notification permission, and the timer isn't yet a dependable background alarm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customize&lt;/strong&gt; changes recipe settings such as labels, counter steps and reminder times; some still use structured text. Changes that could hide data offer to keep the old version as a copy. CSV export exists; mini-app export/import and conversational editing are unfinished.&lt;/p&gt;

&lt;p&gt;Next I want to give someone JackApp without choosing their wording, see what needs correction, and find out which tools they return to. I'd compare the effort—including setup and corrections—with their existing method and a ready-made template.&lt;/p&gt;

&lt;p&gt;Asking Gemma to describe an entire app left me waiting for something I couldn't use. Giving it a smaller job produced tools I could save and inspect. Now I want to find out which ones are still worth opening after the novelty wears off.&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 4 E2B supplies recipe settings and photo-assisted interpretation through LiteRT-LM, and can classify requests when keyword routing is uncertain. Code supplies the working structure and calculations. On-device inference works offline after the one-time download.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with Codex and Claude Code. I directed the project and tested it on my phone.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>android</category>
    </item>
    <item>
      <title>AI-Assisted Photo Culling with an Open Model, Fully Offline? Not a Myth Anymore.</title>
      <dc:creator>Rupam Das</dc:creator>
      <pubDate>Mon, 19 Oct 2026 06:55:00 +0000</pubDate>
      <link>https://dev.to/rudas/ai-assisted-photo-culling-with-an-open-model-fully-offline-not-a-myth-anymore-n3g</link>
      <guid>https://dev.to/rudas/ai-assisted-photo-culling-with-an-open-model-fully-offline-not-a-myth-anymore-n3g</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;p&gt;Meet Meera. She's an imaginary photographer (but 100% inspired by real photographers). Let's take a peek into her imaginary life (which is also 100% inspired by real photographers).&lt;/p&gt;

&lt;p&gt;It's Saturday, close to midnight, and the wedding couple have just left in a car full of flowers. Meera drives home with about 2,500 photos on her memory cards. She has promised the couple a sneak peek by Tuesday, and before she can edit a single frame she has to look at every one of them and decide which one gets to grand finale of the photo album.&lt;/p&gt;

&lt;p&gt;Photographers call that &lt;strong&gt;culling&lt;/strong&gt;, and it's the slow part. During the ring exchange Meera held the shutter down, so now there are nine shots that look almost the same. Only one of them has nobody blinking. The ceremony runs into the portraits with no clear break. Somewhere in those 2,500 files is the one photo of the bride's grandmother laughing, and Meera knows the bride will ask for it.&lt;/p&gt;

&lt;p&gt;AI culling tools exist, but Meera wants a workflow with no account, no recurring AI bill and no photo uploads. If a tool sends images to a server, someone else's computer sees the wedding.&lt;/p&gt;

&lt;p&gt;A tool that does the boring part fast, with no account, no upload, and an AI the photographer actually owns, sounded like a myth. So I built &lt;strong&gt;MythiCull&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;MythiCull runs on the photographer's own computer. It uses an open model, &lt;strong&gt;EmbeddingGemma 2&lt;/strong&gt;, which is downloaded once. After that, it works with the wifi switched off.&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%2Fil2flbkd576wxrfdx8co.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%2Fil2flbkd576wxrfdx8co.gif" alt="MythiCull in action" width="560" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Meera opens MythiCull and selects the folder containing Saturday's photos. MythiCull imports the photos and creates time-based chapters. Meera can browse and rate immediately. The app offers a one-time model download, which she can defer. When she clicks &lt;strong&gt;Analyze photos&lt;/strong&gt;, it creates image embeddings and caches them, skipping completed batches when analysis resumes after an interruption.&lt;/p&gt;

&lt;p&gt;The workflow brings together four things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It splits the day into chapters.&lt;/strong&gt; A gap longer than 15 minutes starts a new chapter by default; chapters with fewer than three photos merge into a neighbour. These are time-based sections, rather than automatic detection of each wedding activity. After analysis, MythiCull also suggests a name for each chapter, like "Ceremony" or "First dance". Meera can take the name with one click or type her own, like "Grandpa's toast".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It groups bursts.&lt;/strong&gt; Consecutive photos no more than 10 seconds apart that have similar embeddings join the same group within a chapter. Because consecutive matches can chain together, a group can span more than 10 seconds. In &lt;strong&gt;Suggested view&lt;/strong&gt;, Meera sees one photo per group with a small badge showing how many are hiding behind it. Those nine ring shots become a single choice, and she can click the badge if she wants to check the other eight.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It suggests a frame.&lt;/strong&gt; Without a description, the suggestion is the sharpest photo in the group. This is not a blink or facial-expression detector. If Meera cares more about feeling than focus, she can enter &lt;code&gt;candid laughter&lt;/code&gt; under &lt;strong&gt;What to look for&lt;/strong&gt; and adjust &lt;strong&gt;Suggested photo priority&lt;/strong&gt; between &lt;strong&gt;Sharper photo&lt;/strong&gt; and &lt;strong&gt;Closer match&lt;/strong&gt;. The pick then moves towards the photo that matches her words best.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It lets her search in plain English.&lt;/strong&gt; She types &lt;code&gt;grandmother laughing&lt;/code&gt;, and the closest matches come first. No tags, and no one had to label anything.&lt;/p&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%2F728ul5ervj5ogpt2gacw.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%2F728ul5ervj5ogpt2gacw.png" alt="Searching in MythiCull" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then Meera culls with the keyboard, which is how most photographers already work. &lt;strong&gt;P&lt;/strong&gt; keeps a photo, &lt;strong&gt;X&lt;/strong&gt; rejects it, &lt;strong&gt;0 to 5&lt;/strong&gt; sets stars, the arrow keys move, and &lt;strong&gt;Ctrl/Cmd+Z&lt;/strong&gt; undoes a mistake. Visible preview controls offer the same decisions and stars. &lt;strong&gt;Z&lt;/strong&gt; switches to cached-preview pixels for inspection, and &lt;strong&gt;Pin reference&lt;/strong&gt; holds another frame beside the current photo for comparison. The preview is capped at 1600 pixels on its long edge; this is not original-resolution RAW inspection.&lt;/p&gt;

&lt;p&gt;When she's done, she has two ways out. MythiCull can write &lt;strong&gt;XMP sidecars&lt;/strong&gt; next to her RAW files, for a later editing workflow. Keeps get a Green label and at least one star; rejects get Red and zero stars. XMP export covers RAW files other than DNG, excludes JPEGs and stars-only decisions, and has not yet been validated in Lightroom Classic or darktable. Or it can &lt;strong&gt;copy the keepers&lt;/strong&gt; into a new folder, with one subfolder per chapter if she wants. Copies retain their original metadata; stars are recorded in a separate manifest. Both exports cover the whole project, regardless of the current chapter, search or filter.&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%2F4x7somczg0bbsh203uat.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%2F4x7somczg0bbsh203uat.png" alt="Exporting in MythiCull" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  It never touches the originals
&lt;/h3&gt;

&lt;p&gt;Wedding photos can't be reshot. So MythiCull treats them carefully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It never moves, renames, edits or deletes a photo. The only file it writes in the photo folder is an &lt;code&gt;.xmp&lt;/code&gt; sidecar.&lt;/li&gt;
&lt;li&gt;If a sidecar is already there and MythiCull didn't make it, MythiCull skips that photo and says so in the summary.&lt;/li&gt;
&lt;li&gt;When copying, it never overwrites anything. If a file name is taken, the copy gets the next available numeric suffix, starting at &lt;code&gt;_1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Meera's choices are safe too. Importing again or indexing again never changes a keep, a reject, a star, or a chapter name she typed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Is It Different?
&lt;/h2&gt;

&lt;p&gt;The big tools are good, and they're more polished than MythiCull is today. The real difference is who owns the AI. With MythiCull, the model is a folder on the photographer's disk, the code is open, and there's no account that can be locked or cancelled.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Photo Mechanic&lt;/th&gt;
&lt;th&gt;Aftershoot&lt;/th&gt;
&lt;th&gt;Narrative Select&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;MythiCull&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AI suggests the best photos&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Groups near-identical bursts&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Splits a shoot into time-based chapters&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search photos in plain English&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Choose picks using a typed mood&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keyboard-driven culling&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Writes XMP sidecars for RAW photos&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exports keepers into chapter-based folders&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Processes photographs locally, offline&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runs without a user account&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open-source application&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses locally inspectable open AI weights&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;small&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;This comparison is based on publicly documented features as of October 2026, not hands-on testing of every tool. ✓ indicates a documented feature; ✗ means the feature wasn't found in the documentation reviewed. MythiCull is still experimental, and some features await real-world validation.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Would Pay for It?
&lt;/h2&gt;

&lt;p&gt;Photographers like Meera. Solo wedding and event photographers shoot thousands of photos per wedding, often work alone, and do most of their culling late at night. Every hour spent culling is an hour they don't spend editing, replying to couples, or sleeping.&lt;/p&gt;

&lt;p&gt;They already pay for this problem, either with their evenings or with a monthly subscription.&lt;/p&gt;

&lt;p&gt;MythiCull runs entirely on the photographer's computer. No servers to maintain, no per-photo API bills. That means it doesn't need a subscription.&lt;/p&gt;

&lt;p&gt;MythiCull is free and open-source today. But imagine a polished, ready-to-install version that a photographer could buy for $49, just once, and keep for good. No monthly subscriptions, no per-photo fees.&lt;/p&gt;

&lt;p&gt;Why $49? It's a price I think could make sense for solo photographers. Low enough to be approachable, while giving me a way to support continued development. And if it saves even a few hours of culling, that one-time cost could easily be worth it.&lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/fwoFIB-vzSQ" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To run it yourself (Python 3.11+):&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/ru-das/mythicull &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;mythicull
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
uvicorn mythicull.main:app &lt;span class="nt"&gt;--port&lt;/span&gt; 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Open &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt;. Accept &lt;strong&gt;Download model&lt;/strong&gt; on startup, or choose &lt;strong&gt;Not now&lt;/strong&gt; and return to it when you click &lt;strong&gt;Analyze photos&lt;/strong&gt;. Create a project with &lt;strong&gt;Choose folder…&lt;/strong&gt;, import the shoot, then analyze it. After the one-time download, the app works offline. A GPU makes indexing much faster, and you can start culling before indexing finishes.&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/ru-das" rel="noopener noreferrer"&gt;
        ru-das
      &lt;/a&gt; / &lt;a href="https://github.com/ru-das/mythicull" rel="noopener noreferrer"&gt;
        mythicull
      &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;MythiCull&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Local-first photo culling for wedding and event photographers. Import a shoot, let it index the photos, then cull with the keyboard. Picks export as XMP sidecars or copied files.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Imports a folder recursively. JPEG, and RAW through the embedded preview. RAW+JPEG pairs are treated as one photo.&lt;/li&gt;
&lt;li&gt;Splits the shoot into story chapters by time gap (default 15 minutes). Chapters with fewer than three photos merge into a neighbour. Chapters can be renamed; renames survive re-runs. After analysis, you can accept suggested chapter names.&lt;/li&gt;
&lt;li&gt;Groups near-duplicate bursts (within 10 s and similar image embedding) and suggests the sharpest frame in each group. The optional “What to look for” description (for example “candid laughter”) and “Suggested photo priority” slider balance sharpness with how well each frame matches the description.&lt;/li&gt;
&lt;li&gt;Per-camera time adjustments for shoots with multiple camera models, without changing the original files or EXIF timestamps.&lt;/li&gt;
&lt;li&gt;Plain-English…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ru-das/mythicull" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;It's MIT licensed and was built during the challenge. The current suite passes 82 tests, with one real-model test deselected by default. The default tests run in seconds because they use a small fake model instead of the real one.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Here's the path a photo takes through MythiCull:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;the photo folder (never changed)
  → find JPEGs and RAWs (a RAW+JPEG pair counts as one photo)
  → read the time each photo was taken
  → make small previews
  → split on gaps longer than 15 minutes; merge chapters under 3 photos
  → EmbeddingGemma 2 turns each photo into a list of 768 numbers
  → photos ≤ 10 s apart with very similar numbers → one burst group
  → the photographer culls → choices saved in SQLite → XMP sidecars or copied files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The back end is Python, FastAPI and SQLite. The front end is plain HTML, CSS and JavaScript with no build step. Even the fonts ship with the app, so the page never reaches out to the internet.&lt;/p&gt;
&lt;h3&gt;
  
  
  One model, four jobs
&lt;/h3&gt;

&lt;p&gt;EmbeddingGemma 2 turns photos and words into numbers that live in the same space. That means a photo of a first dance ends up close to the words "first dance". This one idea does 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;Job&lt;/th&gt;
&lt;th&gt;What gets compared&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Burst groups&lt;/td&gt;
&lt;td&gt;each photo with the one taken just before it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;td&gt;the typed words with every photo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mood pick&lt;/td&gt;
&lt;td&gt;the typed mood with each photo inside one burst&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chapter names&lt;/td&gt;
&lt;td&gt;the whole chapter with 18 wedding and event labels, like "Ceremony" and "Cake cutting"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Search is one line of math. A few thousand photos don't need a special database:&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="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode_text&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matrix&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="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;  &lt;span class="c1"&gt;# vectors are unit length, so this is cosine similarity
&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Scores are for ranking, not for trusting
&lt;/h3&gt;

&lt;p&gt;The most important thing I learned about this model is that a words-to-photo score only means something next to other scores for the same words. A 0.3 for "rings" isn't the same as a 0.3 for "cake". So MythiCull never tells Meera "80% match". It only puts photos in order.&lt;/p&gt;

&lt;p&gt;The mood pick follows the same rule. Inside a burst, it ranks the photos once by sharpness and once by mood, and then blends the two rankings rather than the raw scores:&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="n"&gt;sharp_rank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&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;g&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;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)))}&lt;/span&gt;
&lt;span class="n"&gt;vibe_rank&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&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;g&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;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&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;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&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;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sharp_rank&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vibe_rank&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Photo-to-photo scores are much steadier, so bursts can use a fixed cutoff. I tested it on 354 public HDR+ frames from 40 exposure-bracket bursts, with synthetic capture times. Photos from the same burst scored between 0.973 and 0.999, while photos from different moments scored between 0.54 and 0.68. Cutoffs from 0.85 to 0.94 produced 39 groups against 40 source bursts, and MythiCull uses 0.92. These were easy bracketed frames, not a moving-subject wedding shoot; the threshold still needs validation on that kind of material.&lt;/p&gt;
&lt;h3&gt;
  
  
  Small things that matter
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No float16.&lt;/strong&gt; It can break this model's numbers, so MythiCull uses float32 on a CPU and bfloat16 on a supported GPU, falling back to float32 when needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indexing can stop and restart.&lt;/strong&gt; Progress is saved after every batch. If Meera's laptop dies halfway through, it picks up where it left off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nobody waits for the AI.&lt;/strong&gt; Culling can start right after import. Time-based chapters are available after import; analysis adds similarity groups and suggested names, while search uses the indexed photos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two cameras, one timeline.&lt;/strong&gt; If a second shooter's camera clock is a few minutes off, Meera can set an offset for that camera model and the day lines up again. The original photo time stays untouched. Two bodies of the same model currently share one offset.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It's someone's wedding.&lt;/strong&gt; These photos show families, kids, and guests who never signed up for an AI service. With MythiCull nothing is uploaded, there's no account, and there's no tracking. The photos stay with the person the couple trusted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It works where photographers are.&lt;/strong&gt; Meera can cull on the train home or in a hotel with bad wifi, and she never waits for an upload bar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It keeps the price fair.&lt;/strong&gt; A hosted model charges for every photo it looks at, which adds an ongoing operating cost. An open model on the photographer's own computer only costs electricity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No one can take it away.&lt;/strong&gt; The model is a folder on the photographer's disk. If a company raises its prices or shuts a model down, MythiCull keeps working the same way it did on day one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I could control every step.&lt;/strong&gt; With open weights I could pick the right number format for each kind of device, use the model's own search prompt, and tune the burst cutoff myself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Where a closed model would win:&lt;/strong&gt; a big hosted vision model might name chapters better, and it might judge facial expressions better too. But it would put an upload, an account and a bill between a photographer and their own work. For a tool that sees every photo of someone's wedding day, I don't think that trade is worth it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Validate the XMP sidecars in Lightroom Classic and darktable.&lt;/li&gt;
&lt;li&gt;A one-click installer, so a photographer never has to open a terminal.&lt;/li&gt;
&lt;li&gt;Validate burst suggestions and description-based picks on a real moving-subject shoot, and time an actual photographer's cull.&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; EmbeddingGemma 2 is the only model in MythiCull, and it does four jobs: burst groups, plain-English search, the mood pick, and chapter names. It runs on the photographer's own computer (float32 on a CPU, bfloat16 on a supported GPU, with float32 fallback), is downloaded once, and then works offline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you know a wedding photographer, or you are one, I'd love to hear what it would take to trust a tool like this with a real cull.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Kokishin: an offline Android app that turns everyday oddities into small mysteries</title>
      <dc:creator>Rupam Das</dc:creator>
      <pubDate>Mon, 12 Oct 2026 06:59:50 +0000</pubDate>
      <link>https://dev.to/rudas/kokishin-an-offline-android-app-that-turns-everyday-oddities-into-small-mysteries-4pie</link>
      <guid>https://dev.to/rudas/kokishin-an-offline-android-app-that-turns-everyday-oddities-into-small-mysteries-4pie</guid>
      <description>&lt;p&gt;&lt;em&gt;Built for the Hacktoberfest Open-Source AI Challenge — Week 1: Touch Grass. 🌱&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Kokishin&lt;/strong&gt; is an Android app that turns everyday outdoor details into small mysteries you solve by walking around and looking closely.&lt;/p&gt;

&lt;p&gt;You photograph something odd: a worn bench, a dirt shortcut across the grass, moss on only one side of a wall. An on-device Gemma 4 model turns the photo into a case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a mystery question&lt;/li&gt;
&lt;li&gt;2-3 possible explanations&lt;/li&gt;
&lt;li&gt;a real-world check for each one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you go outside, collect evidence (photos or notes), write a conclusion, and the AI critiques your reasoning. It never reveals the answer. It asks questions, suggests possibilities, and sends you back outside.&lt;/p&gt;

&lt;p&gt;The idea comes from the everyday-mystery spirit of the anime &lt;em&gt;Hyouka&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%2Fh28hnsqz2j5q4yfxg9tj.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%2Fh28hnsqz2j5q4yfxg9tj.gif" alt="Oreki the GOAT" width="498" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the screen should be the shortest part
&lt;/h2&gt;

&lt;p&gt;Nature apps tell you &lt;em&gt;what&lt;/em&gt; something is. Nothing helps you ask &lt;em&gt;why&lt;/em&gt; it is that way, and answering that takes looking, walking and coming back later. That is a natural reason to touch grass.&lt;/p&gt;

&lt;p&gt;So the app is built to get out of the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One main action per screen.&lt;/li&gt;
&lt;li&gt;One "Your first check" card, with the other possibilities behind a tap.&lt;/li&gt;
&lt;li&gt;Every case records time spent investigating versus time on screen, and shows the split once you solve it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why open-source AI matters here
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Offline.&lt;/strong&gt; It works in parks and on trails with no signal. After a one-time model download, the whole loop works in airplane mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private.&lt;/strong&gt; Photos of your street and routes never leave the phone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free.&lt;/strong&gt; No API costs or rate limits, for you or for me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Controllable.&lt;/strong&gt; I decide exactly how the model behaves, so it never spoils the answer. That rule lives in the prompts, not in a server I don't control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are no cloud calls at all. The only network use is downloading the model files on first launch.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it's built
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Expo + React Native + TypeScript, Expo Router, as a development build (Expo Go can't load native modules like this).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mybigday/llama.rn" rel="noopener noreferrer"&gt;&lt;code&gt;llama.rn&lt;/code&gt;&lt;/a&gt; running Gemma 4 E2B (GGUF) plus its mmproj vision file, so the model can read the photo.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;expo-image-picker&lt;/code&gt; (camera only, no gallery), &lt;code&gt;expo-image-manipulator&lt;/code&gt; to shrink photos before inference, &lt;code&gt;expo-file-system&lt;/code&gt; for the resumable model download.&lt;/li&gt;
&lt;li&gt;Cases saved with AsyncStorage.&lt;/li&gt;
&lt;li&gt;Every prompt lives in one file, &lt;code&gt;src/ai/prompts.ts&lt;/code&gt;, and the model's JSON output is parsed with hand-written type guards. If parsing fails, it retries once and then returns a typed error instead of crashing.&lt;/li&gt;
&lt;li&gt;The release APK is built with EAS Build.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ludl-oF3MPM" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Download the APK from &lt;a href="https://github.com/ru-das/kokishin/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;here&lt;/a&gt; and install it on an Android phone (8 GB RAM recommended). The release APK is about 183 MB, and the app downloads the model files on first launch, so use Wi-Fi.&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/ru-das" rel="noopener noreferrer"&gt;
        ru-das
      &lt;/a&gt; / &lt;a href="https://github.com/ru-das/kokishin" rel="noopener noreferrer"&gt;
        kokishin
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Kokishin is an offline Android app that uses an on-device Gemma 4 model to turn everyday outdoor oddities into small mysteries you solve by going outside and gathering evidence. Built for the Hacktoberfest 2026 Open-Source AI Challenge: Week 1.
    &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;Kokishin&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Turn everyday outdoor oddities into small mysteries, then go outside and solve them.&lt;/p&gt;
&lt;p&gt;Kokishin is an Android app for the dev.to challenge &lt;em&gt;Build something with open-source AI at its core&lt;/em&gt; (theme: &lt;strong&gt;Touch Grass&lt;/strong&gt;). Photograph something odd, like a worn bench, a dirt shortcut or moss on one side of a wall. An on-device Gemma 4 model turns it into a case: a mystery question, 2-3 possible explanations, and a real-world check for each. You collect evidence outside, write a conclusion, and the AI critiques your reasoning. It never gives away the answer.&lt;/p&gt;
&lt;p&gt;The screen is meant to be the shortest part of the experience. The app measures investigating time versus on-screen time for every case.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Screenshots&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Home&lt;/th&gt;
&lt;th&gt;Settings&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/ru-das/kokishin/docs/screenshots/home.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%2Fru-das%2Fkokishin%2FHEAD%2Fdocs%2Fscreenshots%2Fhome.png" alt="Home"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ru-das/kokishin/docs/screenshots/settings-dark.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%2Fru-das%2Fkokishin%2FHEAD%2Fdocs%2Fscreenshots%2Fsettings-dark.png" alt="Settings"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;New Case&lt;/th&gt;
&lt;th&gt;Case&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/ru-das/kokishin/docs/screenshots/new-case.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%2Fru-das%2Fkokishin%2FHEAD%2Fdocs%2Fscreenshots%2Fnew-case.png" alt="New case"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ru-das/kokishin/docs/screenshots/case.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%2Fru-das%2Fkokishin%2FHEAD%2Fdocs%2Fscreenshots%2Fcase.png" alt="Case"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Home:&lt;/strong&gt; your saved cases and the "I'm curious!" button.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New case:&lt;/strong&gt; take a photo (camera only) and optionally say what caught your eye.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Case:&lt;/strong&gt;…&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/ru-das/kokishin" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Screenshots
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Home&lt;/th&gt;
&lt;th&gt;Settings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2Fzde5bgljphumyqt4wee7.png" alt="Home" width="720" height="1600"&gt;&lt;/td&gt;
&lt;td&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%2Fwewpeqhgajzq4i5yyvwg.png" alt="Settings" width="720" height="1600"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;New Case&lt;/th&gt;
&lt;th&gt;Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2F6g4bhpxnipygcguabftc.png" alt="New Case" width="720" height="1600"&gt;&lt;/td&gt;
&lt;td&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%2Fsjjdch8icc0kfijif3zv.png" alt="Case" width="720" height="1600"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;I'd like to investigate why the APK is as large as it is (most likely the llama.rn native libraries), and add a custom icon and splash screen.&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 E2B is Kokishin's only AI, and it runs entirely on the phone through llama.rn. Using its vision file (mmproj), it reads your photo of something odd and turns it into a case: a mystery question, 2-3 possible explanations, and a real-world check for each. Later it critiques your written conclusion. The rule is that the AI asks and suggests but never reveals the answer, and it works offline in airplane mode.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hf26challenge</category>
      <category>devchallenge</category>
      <category>ai</category>
      <category>touchgrass</category>
    </item>
    <item>
      <title>PYQed: I built the exam tool my friend needed, with Gemma 4</title>
      <dc:creator>Rupam Das</dc:creator>
      <pubDate>Mon, 05 Oct 2026 06:58:42 +0000</pubDate>
      <link>https://dev.to/rudas/pyqed-i-built-the-exam-tool-my-friend-needed-with-gemma-4-1hnf</link>
      <guid>https://dev.to/rudas/pyqed-i-built-the-exam-tool-my-friend-needed-with-gemma-4-1hnf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Everytime exams come up, my friend Subhadip and the entire class panics. They hop into their WhatsApp group (our holy repository) and browse through PDFs of previous year questions (PYQs).&lt;/p&gt;

&lt;p&gt;There's a huge collection of question papers, but no idea about what to study.&lt;/p&gt;

&lt;p&gt;Which chapters are the most important ones?&lt;br&gt;
What are the maximum-marking questions?&lt;br&gt;
Are these questions repeated?&lt;/p&gt;

&lt;p&gt;We usually take a guess, or randomly refer to a curated list of important questions that some random higher year student has given us.&lt;/p&gt;

&lt;p&gt;When Subhadip had an exam, and my friends continued to pull the same stunt, I thought, why don't we make something that can do this for us?&lt;/p&gt;

&lt;p&gt;So, I built PYQed for Subhadip and others, taking the syllabus and previous year question papers and converting them into a question bank organised by topic and marks.&lt;/p&gt;
&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your syllabus becomes subjects automatically.&lt;/strong&gt; Import the syllabus PDF and PYQed creates a folder for each subject, with its units and topics. One can edit freely, and you can even create subjects manually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It reads scanned papers.&lt;/strong&gt; Most PYQs are images exported as PDF. Gemma 4 reads each page, extracts each question with its number, marks, and type, and you can review them beside the original page before saving.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each question is filed under its syllabus topic,&lt;/strong&gt; marked as the number of marks and sorted by how often they have appeared before. "Asked 3× (2019, 2021, 2024)" appears on the card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Units are prioritized based on actual weight,&lt;/strong&gt; such as average marks per paper, so that one knows where to begin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practice mode shows questions from topics chosen&lt;/strong&gt; and those marked "Revise" appear more frequently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One imports, all benefit.&lt;/strong&gt; A subject exports as a tiny JSON file you can WhatsApp. Anyone who opens it obtains the complete analysis without needing an account or key.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Subhadip's Reaction
&lt;/h2&gt;

&lt;p&gt;I sent Subhadip the APK and a DSA JSON file I'd already analyzed. A few minutes later, he called me and said&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Wait, this question has come up three years in a row and nobody told us? And Unit 3 is worth more than the other two put together?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After that he tried and app by himself, and used the practice mode. His verdict was&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Bro where were you all this time? Share it to the rest ASAP!!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Install and try the app &lt;a href="https://github.com/ru-das/PYQed/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YouTube Video&lt;/strong&gt;: &lt;a href="https://youtu.be/vQk791GQyJ8" rel="noopener noreferrer"&gt;https://youtu.be/vQk791GQyJ8&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Home&lt;/th&gt;
&lt;th&gt;Syllabus review&lt;/th&gt;
&lt;th&gt;Paper review&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2Fdry9scsvk4hjdvwc7pwd.jpg" alt="Home" width="720" height="1600"&gt;&lt;/td&gt;
&lt;td&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%2Fqbc0e4xpsopab2cd1mbf.jpg" alt="Syllabus Review" width="720" height="1600"&gt;&lt;/td&gt;
&lt;td&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%2Fp6v90sg7jgfgpk4n4ecg.jpg" alt="Paper Review" width="720" height="1600"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic view&lt;/th&gt;
&lt;th&gt;All questions&lt;/th&gt;
&lt;th&gt;Practice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2Filyva9trib4km06kmp97.jpg" alt="Topics" width="720" height="1600"&gt;&lt;/td&gt;
&lt;td&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%2Fn17a52cm6ewlq7ymrrrh.jpg" alt="All Questions" width="720" height="1600"&gt;&lt;/td&gt;
&lt;td&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%2Fxvr1eakixx4tk3e02a7h.jpg" alt="Practice" width="720" height="1600"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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/ru-das" rel="noopener noreferrer"&gt;
        ru-das
      &lt;/a&gt; / &lt;a href="https://github.com/ru-das/PYQed" rel="noopener noreferrer"&gt;
        PYQed
      &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;PYQed&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Turn your university syllabus and past year question papers (PYQs) into a topic-wise question bank you can sort, filter and practise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Powered by &lt;a href="https://ai.google.dev/gemma" rel="nofollow noopener noreferrer"&gt;Gemma 4&lt;/a&gt;, an open-weight model from Google DeepMind.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Built for the DEV.to Hacktoberfest Weekend Challenge 2026, theme "Build for a Friend". Built on a 4 GB RAM laptop with no GPU, no server and no budget.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Download&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Get the Android APK from the &lt;a href="https://github.com/ru-das/PYQed/releases/latest" rel="noopener noreferrer"&gt;latest GitHub Release&lt;/a&gt;. On iPhone, run it through Expo Go (see &lt;a href="https://github.com/ru-das/PYQed#setup-for-development" rel="noopener noreferrer"&gt;Setup&lt;/a&gt;).&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Screenshots&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Home&lt;/th&gt;
&lt;th&gt;Syllabus review&lt;/th&gt;
&lt;th&gt;Paper review&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/ru-das/PYQed/docs/screenshots/home.jpg"&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%2Fru-das%2FPYQed%2FHEAD%2Fdocs%2Fscreenshots%2Fhome.jpg" alt="Home screen with subjects" width="240"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ru-das/PYQed/docs/screenshots/syllabus-review.jpg"&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%2Fru-das%2FPYQed%2FHEAD%2Fdocs%2Fscreenshots%2Fsyllabus-review.jpg" alt="Syllabus review screen" width="240"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ru-das/PYQed/docs/screenshots/paper-review.jpg"&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%2Fru-das%2FPYQed%2FHEAD%2Fdocs%2Fscreenshots%2Fpaper-review.jpg" alt="Paper review beside the page image" width="240"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic view&lt;/th&gt;
&lt;th&gt;All questions&lt;/th&gt;
&lt;th&gt;Practice&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/ru-das/PYQed/docs/screenshots/topic.jpg"&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%2Fru-das%2FPYQed%2FHEAD%2Fdocs%2Fscreenshots%2Ftopic.jpg" alt="Topic view with questions sorted by marks" width="240"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ru-das/PYQed/docs/screenshots/all-questions.jpg"&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%2Fru-das%2FPYQed%2FHEAD%2Fdocs%2Fscreenshots%2Fall-questions.jpg" alt="All questions with sort and filters" width="240"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ru-das/PYQed/docs/screenshots/practice.jpg"&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%2Fru-das%2FPYQed%2FHEAD%2Fdocs%2Fscreenshots%2Fpractice.jpg" alt="Practice flashcards" width="240"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The problem&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Before exams, students skim years of PYQs trying to guess what matters, or trust unverified "suggestions" from seniors. PYQed answers it with data: which units carry the most marks, and which questions keep coming back.&lt;/p&gt;
&lt;p&gt;It works for any university's syllabus and paper format. Nothing is hard-coded to one university.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Create subjects…&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/ru-das/PYQed" 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;h3&gt;
  
  
  No server at all
&lt;/h3&gt;

&lt;p&gt;PYQed has no backend. The app speaks directly to Gemma 4 using the user's own free Google AI Studio key, kept secure on the phone. Getting one only needs a Google login and one button. The app can also use OpenRouter or any OpenAI-compatible endpoint, set in Settings. Everything else is on the phone, like reading PDFs with pdf.js in a hidden WebView, storing subjects, ranking, sorting, and practice. It works offline once imported. It's built with Expo (React Native + TypeScript), tested live in Expo Go, and compiled into an APK in the cloud with EAS, because my laptop (4 GB of RAM, a Pentium, no GPU) can't run Android Studio, let alone a model.&lt;/p&gt;

&lt;h3&gt;
  
  
  A rule: AI reads and labels, code counts
&lt;/h3&gt;

&lt;p&gt;In an early test, Gemma 4 invented a heading that wasn't on the page. That influenced this whole app. Gemma has three jobs and each returns JSON that my code validates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the syllabus into subjects, units, and topics.&lt;/li&gt;
&lt;li&gt;Transcribe each paper page into questions, copying text exactly, never solving or rephrasing. If marks aren't printed, it's required to return null , not a guess.&lt;/li&gt;
&lt;li&gt;Label each question with a topic ID from the syllabus.
Everything else is code: marks per unit, times asked, priority, sort order, and even finding repeats (same wording in at least two different years). No number in PYQed comes from an AI; repeats used to be an AI call, but that cost extra requests in a free quota, and could only match wording anyway, so it's done for free in code. Since no model is perfect, the app never fails silently either: unreadable marks show as "? marks", numbering gaps get flagged, and every question is reviewed next to the original scanned page before it's saved.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What went wrong
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A finished answer was thrown away.&lt;/strong&gt; A table-heavy syllabus took 4‒7 minutes, then failed when "request took too long" while AI Studio's usage page showed the tokens had been generated. My 5-minute timeout had cut Gemma off mid-thought. Now the response is streamed and the app only gives up after 90 seconds of silence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma got stuck in a loop.&lt;/strong&gt; The streamed thinking showed it repeating a "Final Check" that topic details were copied exactly. My prompt said to copy exactly plus a ~15 word limit, which can't both be true. The prompt fixed the loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google stopped answers halfway.&lt;/strong&gt; Its copy filter ( RECITATION ) cut off syllabus answers at the same spot twice, and I was throwing away the good part and starting again. Now the complete part is kept, and only the rest is asked for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything was "Unassigned".&lt;/strong&gt; The review screen marked any edited question as user-edited, and the topic labeller skipped those, so reviewed papers never got labelled. Failures were also silent. Now the real error is shown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDF imports hung forever&lt;/strong&gt; because the importer held an old handle that still thought the PDF reader wasn't ready.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can't steer a model's thinking with a prompt.&lt;/strong&gt; My first attempt at telling Gemma how to think made it dump its outline into the answer. "Think briefly, decide once," plus a few tie-breaker rules, worked better.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built the first structure with &lt;strong&gt;Antigravity CLI&lt;/strong&gt; from a spec I wrote, and used &lt;strong&gt;Claude Code&lt;/strong&gt; to find and fix bugs like the ones above. Every design decision, and every test on a real phone, was mine.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Because the whole thing runs for ₹0 and without a server.&lt;/strong&gt; I've no budget and a 4GB laptop. Gemma 4's free tier is the reason a student can build this at all, and why Subhadip and the class can use it without paying anyone. My first hosting plans died on paywalls; an open model with a free key never hit one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Because I can swap the model without rewriting the app.&lt;/strong&gt; PYQed defaults to gemma-4-26b-a4b-it (faster) and lets you switch to gemma-4-31b-it (larger), OpenRouter, or any OpenAI-
compatible server. If Google changes its free tier tomorrow, classmates change one setting. Someone with a spare PC can point the app at their own LM Studio or vLLM and keep their papers off other people's servers entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Because students' papers and syllabi stay theirs.&lt;/strong&gt; There's no PYQed server and no account. Pages go to the chosen provider only during import. Subjects, practice progress, and
shared files live on the phone, and an exported file never contains an API key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Because open models are moving toward the device.&lt;/strong&gt; Today PYQed uses a hosted model to read papers, but nothing in its design depends on that. As phones and open models meet
in the middle, the reading step can move onto the phone without rewriting the app.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;A shared library of analysed subjects per university, so most students never import anything.&lt;/li&gt;
&lt;li&gt;Practice variants with changed numbers, and checking photographed solutions, both of which need much more careful verification than a weekend allows.&lt;/li&gt;
&lt;li&gt;Reading papers fully on-device as small models improve.&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 is PYQed's default AI. It reads syllabi and scanned papers and labels topics, all under the "AI reads and labels, code counts" rule.&lt;/li&gt;
&lt;/ul&gt;

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