Help

Quick start · Widgets · Capital letters, ready-made · Your own symbols · Handwriting input · Games · jatijm · Not available yet · Your data · Devices · Troubleshooting · How it works

Quick start

  1. Reactions or ratings on a page? Open Widgets, try the live one, create yours, paste the two lines it gives you.
  2. Recognize a drawn capital letter? Try the pad on the home page, download one of the three ready-made recognizers, unzip it next to your page, and call recognize(strokes). Each zip has a working index.html.
  3. Recognize your own symbols? Open Character recognition, draw or upload a few samples per symbol, test on the pad, download the zip.
  4. A text field written by hand? The handwriting input is one box you write letters into, one over the last. One script tag.
  5. Just want to play? The games on digitizer.fun are all played by writing, read by millirec.

Widgets: reactions & ratings

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.

  1. On Widgets, create a widget: choose reactions, ratings or both, pick the symbols, and copy the snippet.
  2. Paste the script tag once per page and a placeholder wherever a widget should appear:
    <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.
  3. Keep the edit token the page shows you; it is what lets you change the widget later.

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.

Capital letters, ready-made

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.

optionwhat it isreads rightper letterzip
accuratea tiny net answers 80% of drawings; a larger 4-bit net decides the close calls. Uses the wasm kernel.98.5%0.24 ms41 KBdownload · try
fastthe same shape, escalating only 5% of drawings to a pruned larger net. Uses the wasm kernel.97.8%0.10 ms49 KBdownload · try
smallone tiny net at 4 bits, plain JavaScript, no wasm, no drawing pad: 8 KB gzipped end to end.96.4%0.9 ms11 KBdownload · try
templatesthe stroke matcher on drill templates — the option for lowercase, digits and punctuation too, one set per zip.99% on its writer0.3–0.6 ms30–37 KBcapitals · 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.

Embedding

<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 drawing pad

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.

These read capital letters only, drawn one at a time, and they were measured on many hands rather than tuned to one. A recognizer for your own symbols — or your own alphabet — is what the builder is for.

Your own symbols: the Character recognition page

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.

Handwriting input: one box

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">

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.

Games

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

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.

Not available 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.

Your data

The short version; the privacy page lists every stored item, and the terms say what you may do with the service.

What do the character recognizers and the games send?
Nothing. They are static files that run in the browser; there is no server behind them. The games keep your progress, bests, constructed puzzles and emoji samples in the browser's site data.
What does the builder keep?
Your training samples, in your browser's local storage. Saving a library creates a resumable link on our server holding the library's definition, so you can come back to it; nothing is sent until you save.
What does the handwriting input send?
Nothing while you write. A hosted word list is fetched from our server by its id when the page loads, and saving one from the handwriting input page stores the words you gave it.
What do the widgets send?
The vote — which reaction, or which rating — for a target on a widget. The drawing is recognized on the visitor's device and is not sent. Votes are tied to a random token in the visitor's browser and their address so each visitor counts once; there are no accounts.

Devices

Troubleshooting

The example page shows nothing when I draw.
Open it over HTTP (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.
It reads my letter as something else.
Draw it larger and centred, one letter at a time; the recognizers rescale the drawing but a very small one loses its shape. The pairs it confuses most are the ones people confuse: V and U, O and D, C and L, E and F. In the games, the runners-up stay tappable for a few seconds after a letter is taken; if one letter is consistently wrong for your hand, the builder can train a recognizer on your own samples.
The widget shows no counts.
The script tag needs the host the widget was created on, and the placeholder's data-widget must be its id. Counts arrive after the first vote.
I lost my builder library.
It lives in the browser's site data; clearing that (or private browsing) removes it. A saved library is still at its resumable link.

How it works, for the curious

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).

Beta. If something breaks, that's useful — please tell us what device, what browser, and what you were doing.