Literacy Is Going the Way of Cursive
So I built the bridge tool.
Read This For Me — point a camera at any words and hear them, four different ways, from one photo.
Mark Tornga · marktornga.com
Press F for presenter mode. Arrow keys to advance.
Who It Helps
54 million adults in the U.S. read below a 6th-grade level. Many navigate daily life by asking others for help, memorizing routines, or avoiding situations entirely.
Read This For Me lets people read independently and privately — no accounts, no history, no one watching. Just point at text and listen.
Perfect for reading menus at restaurants, understanding medical paperwork, decoding official letters, and navigating signs and notices.
That is the user today, and it is reason enough to build this. The rest of this section is about why I do not think the problem stays this size.
Everything Written Is Legacy Code
Short-form video already won. An entire generation gets its instructions, its news, and its recipes without reading a paragraph.
So when a process still requires reading — the clipboard of forms they hand you at the doctor's office — that is not a neutral design choice. That is a system written against a runtime that is being deprecated.
// written English, user-facing
// status: deprecated
// shim: LLM translation
// removal: TBD
// we are the shim.Everyone in this room is literate, and was raised in a literate world. That makes this sound insane.
It is going to sound less insane every year.
How a Skill Dies
Everyone can write cursive.
It is simply how writing works.
You should learn cursive because I said so.
The reason is now the authority, not the use.
No one can explain why we’re still teaching cursive.
It survives as ritual, then it stops.
Now run the same three stages on reading.
- 1. Everyone can read. — you are here
- 2. You should learn to read because I said so.
- 3. No one can explain why we're still teaching reading.
The App in Ten Seconds
Point
At menus, forms, mail, medicine labels, signs.
Tap
Clipboard reads every word. Brain explains it.
Listen
Natural voice, adjustable speed, nothing to read.




How It Works
Two paths out of one photo. The pink path never touches a language model. The explain path runs gemini-3.5-flash-lite — a single model now. It used to run a 50/50 split against an older Flash Lite so I could compare latency in production; that older model was deprecated, and the winner is both the survivor and the faster one.
One Capture, Four Outputs — and One of Them Has No AI in It
Read every word
Cloud Vision TEXT_DETECTION → TTS. That is the whole feature.
- No model.
- No prompt.
- No temperature.
- No hallucination surface.
Explain it to me
Cloud Vision → Gemini → TTS, in three lengths.
- Short.
- Medium.
- Long.
- All from one prompt template.
An explanation costs about 470 milliseconds and a fraction of a cent.
So you stop picking one rendering on the user's behalf. You hand them four and let them choose.
And knowing which half of the product should not use the LLM is most of the design. When someone needs the exact wording of a legal notice, the trustworthy answer is the deterministic one.
The Whole Pipeline Is Seven Lines
Real world → data
const request = {
image: { content: image },
features: [{ type: "TEXT_DETECTION" }],
};
const [result] = await client.annotateImage(request);
const fullText = detections[0].description;Data → information
const result = await model.generateContent(prompt);
const response = await result.response;
const explanation = response.text();The first block is the entire “read every word” feature.
Six lines, one API, zero prompts.
There Is Exactly One Prompt
Task: Explain the following text simply and directly for someone with low literacy.
LENGTH: ◄ the only line that ever changes ►
RULES:
- Output ONLY the explanation.
- NO introductory or concluding sentences (e.g., NO "Here is a summary", NO "Important information follows").
- NO markdown formatting (no *, #, -, etc.).
- NO conversational filler.
- Use simple, direct sentences.
TEXT to simplify:
${text}Every line except LENGTH: is fixed. Note that the rules are almost entirely negative — five things not to do, one thing to do. Most of the work in this prompt is suppressing the model's instinct to be chatty at someone who is already struggling.
Shown dedented, with the one long rule wrapped. The real template literal is indented eight spaces per line, and that whitespace is genuinely sent to Gemini.
The Entire Difference Between Short, Medium, and Long
Give a very brief 1-2 sentence summary. Just state what this document is and its main point.
— everything above and below this line is byte-identical —
Give a clear explanation in 3-5 sentences. Cover the key points someone needs to know.
— everything above and below this line is byte-identical —
Give a thorough explanation covering all important details. Be comprehensive but still use simple language.
No maxOutputTokens. No temperature. No generationConfig anywhere in the repo.
The token budget is a sentence of English.
How That Prompt Got There
1f273f5Ask nicely
One sentence. "Explain this text simply to a layperson who cannot read well." It worked, and it rambled.
65e90efAdd negative constraints
The RULES block appears. Structure, and five explicit prohibitions. Still one fixed length.
49aac2aMake length a parameter
"Keep it very concise." is deleted and replaced by the LENGTH slot. This one edit turned one summary into three.
0a7d941Deduplicate
Extracted into a shared buildExplainPrompt() so /explain and /explainPipeline cannot drift apart.
Free-form request → structured constraints → the varying part becomes a parameter. Same arc as any other refactor.
Three Answers, One Photo
The OCR text is cached on the device, so changing length re-calls /explain with the same string. No second capture, no second Vision call.
const handleLengthChange = async (newLength: ExplanationLength) => {
if (newLength === explanationLength) return;
setExplanationLength(newLength);
// If we have original OCR text, re-explain with new length
if (isExplainMode && originalOcrText) {
await stop();
const explanation = await explainText(originalOcrText, newLength);
setActiveText(explanation);
await speak(explanation);
}
};Output 1: Read Every Word
Illustrative example — not a live capturePATIENT REGISTRATION & FINANCIAL RESPONSIBILITY
ASSIGNMENT OF BENEFITS
I hereby authorize payment directly to the
provider of benefits otherwise payable to me,
but not to exceed the provider's regular charges
for this period of treatment. I understand that
I am financially responsible for all charges
whether or not paid by insurance.
NOTICE: Balances not paid within 90 days of the
statement date may be referred to an outside
collection agency. A $35 fee applies to any
returned check or declined payment method.
Appointments cancelled less than 24 hours in
advance are subject to a $50 missed-visit
charge, not billable to your insurance.
Signature ______________ Date __________Cloud Vision output, spoken back verbatim. No model touched this.
This is what you get today if you ask the receptionist to read it to you — assuming you are willing to ask a stranger to read your medical paperwork out loud in a waiting room.
Outputs 2, 3, 4: Same Photo, One Prompt
Illustrative exampleshort
23 wordsThis is a form from the doctor’s office about paying your bill. It says you have to pay whatever your insurance does not.
medium
69 wordsThis is a registration form from the doctor’s office. When you sign it, you let your insurance pay the doctor directly, but you still owe anything the insurance does not cover. If a bill goes unpaid for 90 days, it can be sent to a collection agency. If you cancel less than a day before your visit, you will be charged 50 dollars, and insurance will not pay that.
long
123 wordsThis is a form the doctor’s office needs before your visit. Fill in every part, because a missing answer can delay your appointment. When you sign it, you are telling your insurance company to send payment straight to the doctor instead of to you. You are still responsible for the whole bill. If your insurance pays part of it, or none of it, the rest is yours to pay. If a bill sits unpaid for 90 days, the office can hand it to a collection agency. If a check bounces or a card is declined, they add a 35 dollar fee. If you cancel with less than 24 hours notice, you will be charged 50 dollars, and your insurance will not cover that.
Written by hand for these slides to match each prompt's stated constraint — not captured from a live run. The point is the shape of the four outputs, not the wording.
Where the Time Actually Goes
Median warm latency, measured from 82 production log entries on 19 August 2026.
The first call of the day cost 5,135ms
That is one cold Vision call from this morning's logs — 9x the warm median. Cloud Scheduler now pings the real endpoints every five minutes for about fifteen cents a month. The gotcha: a dedicated warm-up function is a separate Cloud Run container and warms nothing.
Start talking before you finish thinking
TTS runs one sentence at a time, and sentence N+1 loads while N plays. Only the first sentence blocks audio — and that sentence comes back in 228ms. The rest of the pipeline keeps working while the user is already listening.