Quick start · Widgets · Capital letters, ready-made · Your own symbols · Handwriting input · Games · jatijm · Not available yet · Your data · Devices · Troubleshooting · How it works
recognize(strokes). Each zip has a working
index.html.An embeddable reaction bar and star rating where the visitor draws the heart or the star. The drawing is recognized on their device; only the vote reaches our server, which keeps one count per widget, once per visitor, with no account.
<script async src="https://YOUR-MILLIREC-HOST/embed/millirec-widget.js"></script>
<div data-millirec="reactions" data-widget="YOUR-ID" data-target="this-page"></div>
<div data-millirec="ratings" data-widget="YOUR-ID" data-target="this-page"></div>
data-target names the thing being reacted to, so one widget can serve many
pages. reactions-count and rating-average placeholders show the
totals without a drawing pad.A visitor reacting twice toggles the reaction off; rating twice revises the rating. Votes are tied to a token in the visitor's browser and their address, which is how "once per visitor" works without accounts.
Three complete recognizers for drawn capital letters A–Z. Each is a folder with a runtime,
one or two small convolutional nets, a config.js, and a working example. They were
measured on 7,800 test images from hundreds of writers (the EMNIST uppercase set) — none of
which they were trained on.
| option | what it is | reads right | per letter | zip | |
|---|---|---|---|---|---|
| accurate | a tiny net answers 80% of drawings; a larger 4-bit net decides the close calls. Uses the wasm kernel. | 98.5% | 0.24 ms | 41 KB | download · try |
| fast | the same shape, escalating only 5% of drawings to a pruned larger net. Uses the wasm kernel. | 97.8% | 0.10 ms | 49 KB | download · try |
| small | one tiny net at 4 bits, plain JavaScript, no wasm, no drawing pad: 8 KB gzipped end to end. | 96.4% | 0.9 ms | 11 KB | download · try |
| templates | the stroke matcher on drill templates — the option for lowercase, digits and punctuation too, one set per zip. | 99% on its writer | 0.3–0.6 ms | 30–37 KB | capitals · lowercase · digits |
Per-letter times are one recognition in the wasm kernel measured in node; in a browser add a few milliseconds for drawing the strokes onto a canvas. "Reads right" is top-1 accuracy on the test set; the right letter is in the top three 99.7% of the time. The templates option was measured on its writer's own held-out drills, not on many hands.
<script src="config.js"></script>
<script src="millirec-char-cnn.min.js"></script>
<script>
const rec = await MillirecCharCnn.create()
rec.attach(document.querySelector('#pad')) // a drawing pad (not in the small option)
document.querySelector('#pad').addEventListener('millirec:result', (e) => {
console.log(e.detail.results) // [{char, prob} x3], best first
})
const r = await rec.recognize(strokes) // your own strokes: [[{x,y},...], ...]
r.stage // 0 = the tiny net answered, 1 = the larger one
</script>
Serve the folder over HTTP (the wasm kernel does not load from file://).
config.js lists the stages and the escalation threshold; delete the second stage
for a tiny-only recognizer, or remove kernel to run in plain JavaScript (about
17× slower per net, no wasm file). The small option ships the pad-less runtime: capture
strokes yourself and pass them to recognize(); its example page shows the twelve
lines that takes.
The pad that attach() mounts reads one character. Writing in it again after a
two-second pause starts a fresh character (the old ink goes faint first, so you see the wipe
coming), and scribbling over the ink throws it away. Both fire a millirec:clear
event with an undo(), and an empty pad reports a result with no readings so a
page never shows a letter that is gone.
The Character recognition page is the library builder. It makes a self-contained recognizer for symbols you define — a rating scale, a checklist mark, an alphabet — that you host yourself. Draw or upload a handful of samples per symbol, test it live on the page, download a zip of about 80 KB, most of it the matcher. No server call, no API key, nothing to keep running.
Pick drawn strokes only or photos only if you know which you need — each ships
one worker instead of two. A drawn training set can also read photographs; choose both
when you want that. Photo input can also read a whole grid of cells or every blob in a scene,
and, given a baseline, tells c from C by height.
<script src="training.js"></script>
<script src="millirec-char.min.js"></script>
<script>
const rec = await MillirecChar.create()
rec.attach(document.querySelector('#pad'))
await rec.recognize(strokes) // or a canvas / image / file
await rec.recognizeGrid({ canvas, rows: 4, cols: 10 })
</script>
Saving in the builder gives you a resumable link; the training samples stay in your browser. The page offers character libraries only for now — word, sentence and page-cleaner libraries are listed there as coming later. The sudoku's emoji mode (on digitizer.fun) is this builder at work inside a game: nine symbols, a few samples each, a recognizer built on the page.
A text input for handwriting where the writer never moves along the line: each letter is written on top of the last. The widget works out which strokes belong to which letter, greys a letter when it looks complete, erases it once it is sure the next has begun, and after every stroke shows the words the ink could be and the handwritten word so far. Tapping a candidate accepts it; done takes the best reading; undo takes back a stroke.
<script async src="https://YOUR-MILLIREC-HOST/embed/millirec-overlap.js"></script>
<div data-millirec-overlap data-target="#answer"></div>
<input id="answer">
data-set="lowercase|digits|punctuation|all",
or data-alphabet="ABC0123" for exactly those characters.data-words, a hosted list you edit on the
handwriting input page (data-list="ol_…"), or a
text file. A digits box with no usable words runs free: every character priced
alike, which is the mode for numbers and codes., . : without the list naming it; an
apostrophe is read only where the list has it.Measured on its writer's held-out drills, letters written over each other read 96% of
words right for capitals, 99% for lowercase and 99% of random digit strings. The
handwriting input page documents every option and the JavaScript
API; docs/overlap.md in the repository has the measurements.
The handwritten games — sudoku, KenKen, crosswords, wordle, hangman, cryptograms, chess, quizzes and more — are on digitizer.fun, which has their rules and its own help. Every letter and digit in them is read by millirec, on your device. Built with millirec shows which recognizer each game uses, with the code to do the same on your own page.
jatijm is our editor for handwritten pages: notes and a blog written by hand and read as whole sentences, with the recognition running on the writer's own device. millirec is the recognition engine inside it — the same point-cloud matcher and convolutional nets described on this page, reading lines of text instead of one character at a time.
Sentence recognition in your own handwriting, and training the recognizer on your hand, live in jatijm. They are not offered from this site yet.
The first two exist and are measured. None is offered yet; when they are, they will appear in the builder next to the character library.
The short version; the privacy page lists every stored item, and the terms say what you may do with the service.
python3 -m http.server in the folder), not from a
file:// path — the wasm kernel cannot load that way. The small option
has no wasm and does work from a file.data-widget must be its id. Counts arrive after the first vote.The ready-made letters are two convolutional nets in a cascade: a 24-pixel net with about 7 KB of weights answers first, and a 32-pixel net is consulted only when the first one's runner-up is close — the threshold is a measured quantile, not a guess. The larger net was trained quantization-aware at 4 bits, which made it slightly better than its 8-bit version and half the size; the nets run in a small wasm kernel or in plain JavaScript.
Your own symbols use a point-cloud template matcher: each sample becomes fifty oriented points, and a drawing is scored against every template by an assignment cost over position and tangent. It needs only a handful of samples per symbol, which is why the builder can train in the browser — and why the sudoku can learn nine emoji signs from two samples each.
The handwriting input segments the strokes in the box by a dynamic program over stroke positions, scoring every candidate group against full and partial letter templates and pricing each letter by the word list, then erases a letter only when the best reading beats the best reading without that boundary by a margin. What survives is a small beam of prefixes, so a later letter can still repair an early misread.
The games' probabilities — wordle's colours and hangman's masses — are the recognizer's top three readings, renormalised and hedged with a temperature, so that a 95/5 reading counts about 80/20. The quizzes match a reading to the answers with an edit distance whose substitution costs come from the letters the recognizer actually confuses on its own test set.
Every option offered here was measured on held-out writing before it was offered, and the
measurements — including the ones that went against our expectations — are written up in the
repository's docs/ directory (char-lite.md for the letters,
overlap.md for the handwriting input).