DEV Community

Cover image for Ditto: type it once, and Gemma 4 on your laptop fills every form after that
Vivaan Teotia
Vivaan Teotia

Posted on

Ditto: type it once, and Gemma 4 on your laptop fills every form after that

Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady Submission 🦥

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady

What I Built

In school, when an answer was the same as the line above, you didn't write it again. You drew two little strokes, 〃, the ditto mark, and moved on.

The internet never learned that trick. Job portals, bank KYC forms, clinic intakes, college applications and rental forms all ask the same forty questions in slightly different words. Legal first name. Father's / Spouse's Name. Date of Birth (DD/MM/YYYY). Aadhaar No. (last 4 digits only). Is your correspondence address the same as your permanent address? Browser autofill handles your name and email, then gives up, and you type the rest again. Every form. Forever.

So I built Ditto: a Chrome extension that fills any web form from a private profile, using Gemma 4 running on my own laptop through Ollama. Press Alt+Shift+. (or click the toolbar button) and a small open model reads the form the way you would (labels, sections, instructions) and fills it, field by field, at sloth speed, while you do nothing:

  • 🟢 Green: came from your profile.
  • 🟠 Amber: please check. Gemma wasn't sure, or the value couldn't be traced back to your profile.
  • ⚪ Grey: needs you. Essays, consent boxes, and things your profile doesn't know yet.

It's designed to be safely lazy:

  • It never submits a form and never ticks "I agree" or "I hereby declare". Consent is yours to give.
  • It never touches passwords, card numbers, OTPs or captchas.
  • It doesn't invent. If your profile doesn't say how many people will live in the flat, that field doesn't get a confident guess.
  • It gets lazier over time. Type a missing answer once, click Remember what I typed, and Gemma turns it into a reusable fact. A form it has seen before fills in under a second.
  • Even setup is lazy: paste your resume into the profile page and Gemma extracts the facts for you to approve.

Who gets to be lazier? Anyone who fills forms, which is everyone. Job seekers retyping their resume into applicant-tracking portals. Students on admission and scholarship forms. Parents on school forms. Patients at a new clinic. And anyone opening a bank account in India, where KYC is less a process and more a lifestyle.

Demo

Ditto filling a 26-field bank account opening form

A real run on my laptop's CPU, with the waiting sped up 5×. Watch for the BLOCK LETTERS, the DD/MM/YYYY dates, "last 4 digits only" for Aadhaar, the income bracket picked from the salary, the permanent address (Chennai) kept apart from the current one (Bengaluru), and the nominee section filled with Meera's mother, not Meera. The declaration checkbox is left for the human.

🌐 Website and five demo forms: dragon-slayer27.github.io/ditto. Install the extension, click Load Meera (a fictional person with about 50 facts) and try any of the forms.

It learns from you

I removed three facts from Meera's profile (blood group, allergies, emergency contact phone) and opened a clinic form. Ditto left those fields empty instead of guessing. I typed them in by hand and clicked Remember what I typed:

Ditto proposing three new facts to remember

Ditto proposed exactly those three facts. It deliberately ignores free-text answers, like the reason for a clinic visit, because those only make sense once. Then I opened a completely different form, a hackathon registration, and the allergy and emergency contact phone filled themselves:

The next form filling with the learned facts

That's the slow and steady part: every form you fill by hand is the last time you type those answers.

Code

GitHub logo dragon-slayer27 / ditto

Type it once. A Chrome extension that fills any web form from your private profile using Gemma 4 running locally via Ollama.

Ditto 〃 type it once

A Chrome extension that fills any web form from your private profile, using Gemma 4 running on your own computer through Ollama. Job portals, bank KYC, clinic intake, rental applications, college forms: they all ask the same forty questions in slightly different words. Ditto answers them for you, and your passport number never leaves your laptop.

CI License: MIT Model: Gemma 4 (Apache 2.0)

Ditto filling a bank account opening form

A 26-field bank form filled by Gemma 4 E4B on a laptop CPU. Green came from the profile, amber means "please check", grey means "needs you".

Website and demo forms: https://dragon-slayer27.github.io/ditto/

Why

Browser autofill knows your name and email. It has no idea what "Father's / Spouse's Name", "Aadhaar No. (last 4 digits only)" or "Name as per PAN, in BLOCK LETTERS" means, so you type it all again, on every form, forever.

You could paste the form into a cloud chatbot along with your ID numbers, address and…

How I Built It

The open pieces:

  • Gemma 4 E4B (open weights, Apache 2.0) does three jobs: reading forms and choosing values, turning typed answers into profile facts, and extracting a profile from a pasted resume.
  • Ollama runs it on my laptop's CPU (Intel Core Ultra 5, 16 GB RAM, no GPU).
  • A Chrome MV3 extension in about 2,300 lines of plain JavaScript, with no framework and no build step.
  • Playwright drives the real extension through an automated eval, and GitHub Actions runs everything from unit tests to Gemma itself.

What happens when you press the shortcut:

  1. A content script reads the form like a person would. For each field it finds the label (from <label>, ARIA, neighbouring table cells, nearby text or placeholders), the section it sits in, the options, and hints like "(DD/MM/YYYY)" or maxlength.
  2. The background worker sends Gemma your profile and the field list, together with a JSON schema that has one required key per field.
  3. Values stream back, and each field fills the moment its value is complete.
  4. Code checks every value: format rules, option matching, the browser's own validation, and a "tripwire" that traces the value back to your profile.
  5. Answers are remembered per page, so the same form is instant next time.

Six things that broke, and what fixed them

1. Ollama said 403. Ollama rejects requests from browser extensions unless you set OLLAMA_ORIGINS. I didn't want anyone editing environment variables, so Ditto adds a declarativeNetRequest session rule that removes the Origin header only for the extension's own requests (tabIds: [-1]). Zero setup, and web pages still can't reach your Ollama through Ditto.

2. 134 seconds for one form. My first output format was an array of {id, value, from, sure} objects, about 45 tokens per field at about 10 tokens a second. Switching to a per-form schema, {"values": {"d1": …, "d2": …}, "unsure": [...]}, cut the output 3.5× and the same bank form went from 134 s to 51 s. A bonus: every field id is a required key, so the model can't skip a field or invent one.

3. Someone's height came out as "},{". Every number field got junk like ", ". The parser was fine. Gemma really wrote it. My schema only allowed a string or null, the model wanted to write a bare 162, the grammar blocked that token, and the model derailed. Allowing numbers in the schema fixed all seven fields at once. A good reminder that constrained decoding can push a model off a cliff when the schema fights its instincts.

4. Gemma forgot BLOCK LETTERS after section 1. The values were right, just in normal case. Instead of pleading harder in the prompt, Ditto spots "BLOCK LETTERS" on the page and uppercases in code. That became my rule for the whole project: the model finds the facts, code enforces the formats.

5. Confident guesses. Asked for "Monthly take-home income", Gemma divided the annual salary by 12 (ignoring tax). Asked "Number of people who will live here", it said 1. Neither is in the profile. So every value now goes through a tripwire: every word and number in it must trace back to the profile (dates in any format, digit runs as substrings, short numbers as standalone tokens). Both guesses now turn amber on their own.

6. My own shortcut didn't work. The first time I installed Ditto in my everyday browser, the toolbar button worked but Alt+Shift+D did nothing. Dark Reader already owned that combination, and Chrome doesn't warn anyone: it just leaves the new extension's shortcut empty. A GitHub code search found 488 public manifests suggesting Alt+Shift+D and 23 suggesting Alt+Shift+Period, so the default is now Alt+Shift+.. More importantly, Ditto now asks Chrome which shortcut it actually got (chrome.commands.getAll()). If the answer is "none", the popup says so and links straight to chrome://extensions/shortcuts. A CI test reproduces the conflict with a stand-in extension to keep it that way.

The eval: 5 forms, 114 fields, lots of traps

I built five demo forms to be annoying in the ways real forms are:

  • a React job portal, where React's own state must receive the values, not just the screen;
  • an old table-layout bank form with no <label> tags, BLOCK LETTERS and a nominee section;
  • a clinic intake with a date picker, a computed age and two different "Phone" fields;
  • a rental form with placeholder-only inputs and questions the profile can't answer;
  • a dark-mode hackathon form whose radio buttons hide the real <input>s.

That's 114 fields of ground truth, 18 of which must stay empty because the honest answer is "the profile doesn't know". eval/run-eval.mjs loads the real extension in Chromium, fills every form, and scores each field.

Form Fields Right Wrong but shown green First fill Same form again
Job application (React) 28 28 0 50 s 0.1 s
Bank KYC (table layout) 26 26 0 54 s 0.1 s
Clinic intake 23 23 0 45 s 0.1 s
Rental application 18 16 0 27 s 0.1 s
Hackathon sign-up 19 19 0 26 s 0.1 s
Total 114 112 (98.2%) 0 202 s 0.4 s

"Right" counts fields filled correctly and fields correctly left empty. The two wrong answers are the take-home pay and the head count from above, and the tripwire flagged both amber. Not one wrong value was shown green. React's state received all 23 values Ditto typed into the job form.

How small can the sloth be?

I ran the same eval with the smaller Gemma 4 E2B (4.6 GB instead of 6.1 GB):

Model Right Wrong but shown green Output speed on my CPU
Gemma 4 E4B 112 / 114 0 9.7 tokens/s
Gemma 4 E2B 104 / 114 8 11.9 tokens/s

E2B's big mistake was instructive: it put the current Bengaluru address into the bank form's Permanent address section. Those are real facts in the wrong place, so my tripwire, which catches facts that aren't in your profile, couldn't see them: six silent mistakes in one go. That's why Ditto picks E4B when it's installed, and why I measured both on my own forms instead of trusting a leaderboard.

The eval runs in the cloud too: a GitHub Actions workflow installs Ollama on a standard runner, pulls Gemma and fills all 114 fields with the real extension. Its first run finished in under five minutes and reproduced the finding on different hardware: E2B scored 106/114, with the same permanent-address mistake.

Why Does Open Innovation Matter?

Forms are where we hand over our most sensitive facts. PAN, Aadhaar, passport number, home address, medical conditions, family members. I'd never paste those into a cloud chatbot every time a form asks. With an open-weight model on my laptop, the profile lives in my browser, the model reads it locally, and the only place Ditto's manifest lets it connect to is my own machine (127.0.0.1), where Ollama runs. It can't even read a page until you press the button.

It costs nothing per form, forever. No API key, no subscription, no per-token bill, nothing that can be repriced or switched off. That changed how I built it, too: I ran the full 114-field eval over and over while tuning prompts and schemas, for free, on the same machine that will run it for real.

I could open the hood when things broke. The "},{" bug was a fight between the model and the grammar constraining it. I found it by reading raw model output and fixed it by changing the schema. Local control over decoding (JSON schemas, temperature, context size, how long the model stays warm) is what made Ditto reliable rather than lucky.

I could choose the model's size. E2B for 8 GB machines, E4B for accuracy, measured on my own task. A closed API picks that trade-off for you.

Anyone can check it. MIT-licensed code, Apache-licensed weights. If a tool is going to fill in your passport number, you should be able to read every line that touches it.

And honestly, it fits the theme: at about 10 tokens a second, Gemma on my CPU is a sloth. That's fine. It's a sloth that works while I make chai, and it only has to be slow once per form.

Building It With GitHub Copilot

I built Ditto with GitHub Copilot's agent as my pair programmer, from start to finish: brainstorming ideas, benchmarking Gemma 4 on my CPU before writing any code, building the extension and the eval harness, and debugging the "},{" bug from raw model output.

The Lazy Payoff

Across the five demo forms, Ditto fills 95 fields: 1,352 typed characters plus 24 dropdown, radio and checkbox choices. Another 19 fields need a person no matter what (essays, consent boxes, questions the profile can't answer), so I left them out of both sides.

To estimate the by-hand time without fooling myself, I used the Keystroke-Level Model, the classic HCI method for predicting how long a skilled user takes with a keyboard and mouse (0.28 s per keystroke at about 40 wpm, 1.1 s to point, 1.35 s to recall or decide), plus 15 s for each value nobody knows by heart, like a PAN or a policy number. The Ditto side uses the times measured in the eval.

Five forms by hand With Ditto
Your time 11 min 26 s to 14 min 25 s 1 min 06 s (press the shortcut, glance at green, check amber)
Wall clock, first time same as above 4 min 28 s, and you're free while the sloth works
Bank form alone 2 min 35 s to 3 min 20 s 14 s of your time

That's roughly a 10× cut in hands-on time, and the model is generous to the human: it assumes no typos, no switching tabs to copy an address, and no hunting for your PAN card for longer than 15 seconds. If you apply for jobs, a portal asks most of these questions again on every application: 20 job forms like the demo one take about an hour of typing by hand, and about 6 minutes of glancing with Ditto.

The calculation is a script in the repo (eval/payoff.mjs), with every assumption written down next to the per-form table.

Prize Categories

  • Best Use of Gemma: Gemma 4 E4B is the core of Ditto. It reads arbitrary forms in context (sections, instructions, formats), turns typed answers into reusable facts, and extracts profiles from pasted text, all locally through Ollama. I measured E4B against E2B on 114 fields and designed around the model's strengths (structured output) and weaknesses (formatting drift, arithmetic) with guards in code.
  • Best Use of GitHub Copilot: built end to end with GitHub Copilot's agent, and automated with GitHub Actions: CI loads the real extension in Chromium, checks extraction on all five forms and reproduces the shortcut conflict from above; a Model eval workflow installs Ollama, pulls Gemma and runs the full 114-field eval in the cloud, posting the scorecard to the job summary; Pages deploys the site, and tagged releases ship the extension zip.

Solo project. Thanks for reading, and may you never type your information into a form again. 🦥

Top comments (2)

Collapse
 
ekomjah profile image
Ekomjah Denis •

This is a really interesting niche to pick on. Great job thinking out the 'good' lazy

Collapse
 
vivaan_teotia_ profile image
Vivaan Teotia •

Glad you found it worth reading!