Handwriting (ink) data
Last verified against the code on 2026-09-18. Every claim below is a statement about what the code does today, checked at the line level. Where something is a limitation rather than a guarantee, it says so.
Children write by hand in Studyfin: answers on the "Write It" pad, letter tracing, the scratchpad. Handwriting is unusually personal data — it can identify a child and, in a school setting, it may sit alongside an IEP. This document states plainly what is captured, what leaves the device, what is kept, and what is not.
The short version
| Stroke data (the raw path of the finger/stylus) | Never leaves the browser. Never stored by Studyfin. |
| The written image | Sent to the AI checker only when AI checking is needed, and only for that request. Not stored by Studyfin. |
| What Studyfin keeps | Numbers only — a neatness score and per-letter accuracy counts. No image, no strokes. |
| Personalised recognition | Stays in the browser's local storage on that device. Never uploaded. |
What happens when a child writes
1. The ink is drawn and read in the browser. assets/write-pad.js collects strokes into a <canvas>. Recognition is attempted on the device first by assets/digit-recognizer.js, which costs nothing and sends nothing. For a large share of answers — digits, short words, many STEM forms — the answer is checked entirely on the device and no image is transmitted at all.
2. If the on-device recognizer cannot decide, the image is sent for AI checking. The canvas is flattened to a PNG (toDataURL) and passed to the handwritingCheck cloud function, which forwards it to Google's Gemini API for reading and coaching. This is a per-request transmission: the function performs no database write and no file storage — verified by reading its whole body, which contains no .set(), .add(), file.save() or bucket access. The image is not retained by Studyfin.
3. What is saved to the child's record is derived numbers only. saveHandwritingProgress accepts an aggregate and passes it through _validateHandwriting, which allows through only:
- a trend object of five numbers (
count,avgNeatness,recentAvg,earlierAvg,delta) - a per-character heatmap, capped at 60 entries, each
{attempts, correctRate, avgNeatness}
Anything else in the payload is dropped by the server. There is no field in which an image or a stroke path could be stored, by design rather than by policy.
4. Personalised recognition stays on the device. When a child's writing is confirmed correct, the recognizer stores a compressed 196-bit pattern of that glyph in localStorage under sfhwlearn_v1 (max 12 per character, a few kilobytes total) so recognition improves with use. This is never uploaded. Clearing the browser's site data removes it. It exists so more answers resolve on-device, which also means less is sent to the AI.
Who else sees it
Google (Gemini API) is the only third party that receives handwriting images, and only in case 2 above. Studyfin does not control Google's processing; what we can state is what we send (a single image plus the question text) and that we send it only when on-device recognition is insufficient. Schools evaluating this should review Google's terms for the Gemini API directly rather than rely on our summary of them.
Teachers and parents see the derived numbers — neatness trend, per-letter accuracy — and a handwriting goal if one has been set. They do not see stored images, because there are none.
A handwriting goal (students/{sid}.handwritingGoal) is text written by a teacher, therapist or parent. The server refuses a write from the student themselves; a child may read their own goal but not edit it. Every write records who made it.
What we do NOT claim
Being explicit, because a privacy document that overstates is worse than none:
- We do not claim any certification. Studyfin holds no SOC 2, ISO 27001, iKeepSafe,
Common Sense or TrustEd Apps certification for this or any other subsystem. See /trust for the current, honest certification position.
- We do not promise a retention period for data we do not hold. We cannot delete an
image from a third party's systems on your behalf, and we do not imply otherwise.
- We make no claim about on-device learning surviving. It is browser local storage:
it disappears if site data is cleared, and it does not follow a child to another device. It is a convenience, not a record.
- This is not a substitute for a DPA. Schools and districts that need contractual
commitments should request one rather than rely on this document.
For a school or district reviewer
The three questions usually asked, answered directly:
1. Is children's handwriting stored? No image and no stroke data is stored by Studyfin. Only derived numeric scores are, on the student record. 2. Does it leave the country / go to a third party? Images go to Google's Gemini API when on-device recognition is insufficient, and only then. Nothing else receives them. 3. Can we turn the AI path off? Not as a per-school switch today. This is a genuine gap; on-device-first routing exists and is being widened (see docs/HANDWRITING-AUDIT.md, sections A and H), but there is currently no school-level control that forces on-device-only. Do not let anyone tell you there is.
Where to check these claims in the code
| Claim | File |
| Ink collected in-browser; only preferences persisted locally | assets/write-pad.js |
| On-device recognition; learned glyphs in localStorage, never uploaded | assets/digit-recognizer.js (LSKEY = "sfhwlearn_v1") |
| AI check transmits the image and stores nothing | functions/index.js → exports.handwritingCheck |
| Only numbers are persisted | functions/index.js → _validateHandwriting, exports.saveHandwritingProgress |
| Goal is grown-up-write, child-read | functions/index.js → exports.hwGoalSave / exports.hwGoalGet |