This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend
Last month Ma forwarded me a WhatsApp message saying her SBI account would be blocked unless she updated her KYC. She hadn't clicked the link. She had called the number.
My mom reads Bengali. So do my grandparents. Most of the paper that runs their lives doesn't: electricity bills, bank letters, the strip on a blood-pressure tablet, and the steady stream of "Dear Customer, your account will be BLOCKED today" messages that every Indian parent's phone collects.
So every one of those ends up as a photo sent to me, with "এটা কী?" (what is this?). If I'm in a meeting, it waits. If the message is a scam, waiting is the dangerous part.
চিঠি (Chithi) means "letter" in Bengali. It does one thing. You point the phone at any piece of paper or any screenshot, and it says out loud, in your language: What it is. "This is your CESC electricity bill." What you need to do. "Pay ₹1,182.00 by 12-10-2026." Whether it's a trap. A red প্রতারণা! (scam!) stamp, the words "Do nothing. Don't tap the link, don't call the number in the message, don't share an OTP," and one big button that calls me.
It started as a Bengali app for my family. Then I realised the same problem exists in every Indian language, so it now speaks all 22 languages in India's Eighth Schedule, plus English.
Try it: chithi-teal.vercel.app. Pick a language, then tap one of the sample photos (a scam SMS, a bill, a medicine strip), or upload your own.
| Pick a language | Bengali home | A scam | A bill | |---|---|---|---| | | | | |
The public demo runs Gemma in the cloud so you can try it without installing anything, and the page says so. At home it runs on our own laptop. More on why that difference matters below.
It's small on purpose: one Bun server, a phone web app with no framework, and under 200 lines of shared logic for the prompt, cleanup and scam rules.
The model. Gemma 3 4B, an open-weight vision model, running in Ollama on my 8 GB M3 MacBook. The phone sends a photo over home Wi-Fi. Gemma reads it and answers in a fixed JSON shape: original text, document type, a two-line explanation, what to do, amount, deadline, and a risk level of safe, caution or scam. Ollama's JSON-schema mode means the reply always parses.
The voice. The phone's own text-to-speech in bn-IN (or hi-IN, ta-IN, …). It works offline. Languages without a phone voice fall back to one that reads the same script, so Maithili goes through the Hindi voice and Konkani through Marathi.
The phone app. A tiny web app you add to the home screen. Giant buttons, big type, and nothing to learn. It has one button that opens the camera and one that picks a screenshot.
Most of the work was not "call the model." It was finding the ways a 4B model fails, then making sure those failures can't reach my mom. The model caught the scam, then gave the scammer's number. My first test was a fake SBI "KYC update" SMS. Gemma correctly said scam, then advised: "call the helpline number in the message to find out more." That helpline is the scammer. Since then, when the verdict is scam, the advice is fixed text written by a person in each language. The model never writes it. A small model shouldn't be the only guard. A deterministic rule layer in scam.ts checks the transcribed text for OTP and PIN requests, KYC, "account blocked", lottery and prize wording, short links, .apk files, and remote-access apps like AnyDesk. It can raise the risk level and never lower it. A printed MRP on a medicine strip is recorded, not escalated. Numbers are too important to paraphrase. Gemma once read ₹1,182 aloud as "one thousand one hundred eighty-eight". Now the explanation is forbidden from containing numbers at all. The amount and due date come from separate fields copied exactly as printed, and the app shows them in their own boxes. Small models drift between scripts. Bengali answers picked up Korean characters. Malayalam answers picked up Romanian and Russian words. Now any letter that isn't in the reader's own script (or plain English, for brand names like SBI) is stripped, and a badly drifted answer gets one retry. Without the stripping, the phone's Bengali voice would stumble over Hangul mid-sentence. An 8 GB laptop has rules. The first photo took almost four minutes while the model loaded. Then one of my fixes made things worse: the warm-up request and the real one used different context sizes, so Ollama loaded the model twice and timed out. Now the server warms the model at startup with the same settings and keeps it loaded. A photo takes 25 to 60 seconds at home, which is slow for an app and fine for a letter.
On first launch, Chithi shows every language in its own script, with the phone's language suggested first. After that, everything follows the choice: the buttons, Gemma's explanation, the scam warning, the voice, the font, and right-to-left layout for Urdu, Kashmiri and Sindhi.
I was honest with myself about quality. On the 4B laptop model, Hindi, Bengali, Marathi, Telugu, Tamil, Gujarati, Urdu and Kannada are good. Odia and Malayalam were weak, and Gemma 3 4B can't write Santali's Ol Chiki script at all. Those languages carry a "beta" label and an on-screen warning to double-check with family.
Judges can't come to my house, so the demo runs the same code on Vercel. The only difference is where Gemma lives: Gemma 4 26B (a mixture-of-experts model with 4B active parameters) on Google's free Gemini API tier. Two things I learned: Gemma 4 thinks before it answers, and it ran out of room. The first deploy failed because the model spent 1,497 of its 1,500 output tokens thinking and never wrote the JSON. Reading a bill aloud doesn't need deliberation, so I set thinkingLevel to minimal. Replies went from 40 to 55 seconds down to about 5 seconds. The bigger model closes the language gap. In 63 live runs across 16 languages, every scam was caught and every amount and due date was exact. Malayalam and Odia came out clean, and Santali came back in proper Ol Chiki. Same open model family, bigger size, no beta label needed.
Because the photos are my mom's bank letters and prescriptions. At home, the photo goes from her phone to a laptop in the next room and stops there. There's no account, no API key, and no company's servers. A closed API would mean sending a parent's KYC details to a third party to protect them from people who want their KYC details.
