The advice everyone gives about scanning text is to use the highest resolution you can and get the lighting right. I ran the engine this site uses against a set of deliberately damaged images to find out which of those matters, and the answer was neither. The same sentence came back perfectly from a picture a third of the original size, perfectly from grey text on a grey background, and perfectly from a JPEG saved at quality 25. What wrecked it was tilting the page by five degrees. Every number below came out of that run, and they point at a different checklist than the usual one.
TL;DR: Tesseract reads cleanly rendered text until two specific things go wrong. Tilt is the first: 0 to 3 degrees of skew gave a 0% character error rate, 5 degrees gave 2.9%, 12 degrees gave 16.7%, and 20 degrees gave 32.4%. Size is the second, and it is a cliff rather than a slope: text stayed perfect down to a capital height of about 7 pixels and then collapsed to 85.3% errors at 5.8 pixels. Low contrast, blur and heavy JPEG compression barely registered. Straighten the picture before you do anything else. The image to text converter runs the engine in your browser, so the picture is never uploaded.
How were these numbers produced?
By rendering a known sentence, damaging the image one way at a time, reading it back, and counting the edits needed to repair the result.
The metric is character error rate: the Levenshtein distance between what the image said and what the engine returned, divided by the length of the truth. A 2% error rate on a line of fifty characters is one wrong character. The text was three lines mixing prose, an invoice number, a date, a currency amount, an email address and a phone number, because the mistakes that matter in practice happen in the second kind of content rather than the first.
One caveat belongs at the top rather than buried. These are rendered images, not photographs. Each one isolates a single variable, which is the only way to attribute a failure to a cause, but a real photo of a page arrives with several at once: a slight tilt, uneven lighting across the fold, motion blur at one edge, and a camera that sharpened everything before saving. The thresholds below are therefore best cases. A photograph that is right at the limit on two axes will do worse than either number suggests.
The engine is Tesseract 4's LSTM model through tesseract.js, which is exactly what the tool runs, so these are not numbers from a different system.
How much skew is too much?
Between three and five degrees, which is less tilt than you would notice.
| Rotation | Character error rate | Reported confidence |
|---|---|---|
| 0 degrees | 0.0% | 95 |
| 1 degree | 0.0% | 95 |
| 2 degrees | 0.0% | 95 |
| 3 degrees | 0.0% | 94 |
| 5 degrees | 2.9% | 91 |
| 8 degrees | 7.8% | 82 |
| 12 degrees | 16.7% | 70 |
| 20 degrees | 32.4% | 66 |
Nothing happens for three degrees and then it falls off a shelf. The reason is in how the engine works: it finds lines of text, then slices each line into characters. A tilted line crosses several rows of pixels, so the slicing cuts through the tops of some letters and the bottoms of others, and the model is asked to classify fragments.
At 5 degrees the errors were small and expensive. "dated" came back as "dateq" and "total" as "totg)". Both are a single letter inside a word a human would read straight through, and both sat next to an invoice number and a currency amount that a person was going to copy without checking.
A phone held by hand over a page is routinely out by more than five degrees. This is the single thing worth fixing before you run anything, and the fix is free: rotate the picture until the lines are level.
How small can the text be before OCR fails?
Smaller than you expect, right up to the point where it stops working entirely.
| Image width | Capital letter height | Error rate | Confidence |
|---|---|---|---|
| 1600 px | ~29 px | 0.0% | 95 |
| 800 px | ~14 px | 0.0% | 95 |
| 533 px | ~10 px | 0.0% | 92 |
| 400 px | ~7 px | 0.0% | 84 |
| 320 px | ~6 px | 85.3% | 41 |
| 240 px | ~4 px | 100.0% | 0 |
The same three lines read perfectly at a quarter of their original size, where each capital letter is about seven pixels tall. One step further down and the output was unusable, then empty.
That is a cliff, not a gradient, and it changes the advice. "Use the highest resolution you have" is not wrong, but it describes a problem most pictures do not have. A screenshot of a web page, a photo of a sign, a phone picture of a printed letter: all of these have far more than seven pixels of letter height. What they more often have is a tilt.
The place the cliff does bite is a crop from a distance. Text in the background of a holiday photo, a sign across a street, a line of small print inside a full-page scan at screen resolution. If the letters are hard for you to read at 100% zoom, the engine is near its floor too.
Does low contrast ruin the reading?
No, and this was the result that most contradicted what I expected.
Grey text at #8a8a8a on a grey background at #bdbdbd read back with a 0.0% error rate and a confidence of 95, identical to black on white. A three pixel Gaussian blur over the whole image gave 0.6%. A JPEG saved at quality 25 and scaled to half size gave 0.0%.
The reason is that the engine does not read your greys. It binarises the image first, turning everything into black and white with a threshold chosen from the picture, and uniform low contrast survives that step intact. What does not survive is contrast that varies across the page, which is what a shadow or a phone flash produces: one threshold cannot suit both halves of the image.
So the useful version of the lighting advice is narrower than the usual one. Even lighting matters. Bright lighting does not. A dim, evenly lit photo of a page beats a bright one with a shadow down the middle.
Why does a two-column layout come out scrambled?
Because reading order is a layout judgement, and the engine makes it from geometry alone.
I rendered two blocks of text side by side, three lines each, on shared baselines. The result came back interleaved:
LEFT ONE RIGHT ONE LEFT TWO RIGHT TWO LEFT THREE RIGHT THREE
Every word is correct. The order is useless. The engine found six lines of text sitting at three vertical positions and read each row across, which is the correct behaviour for a table and the wrong one for two columns of prose.
A real newspaper page or academic PDF often survives this, because the gutter between columns is wide and consistent enough for the page segmentation step to detect. A screenshot of a layout with two loose blocks beside each other, which is the common case for this kind of tool, often does not.
The reliable fix is to crop. Run one column, copy the result, then run the other. It sounds laborious and takes about fifteen seconds, which is less than rebuilding a paragraph whose sentences have been shuffled.
Can it read handwriting?
Not reliably, and the boundary is about whether the letters touch rather than whether they look informal.
I could not test real handwriting without supplying a photograph, which would have mixed variables, so I tested three fonts of decreasing regularity as a stand-in:
| Font | Error rate | Confidence |
|---|---|---|
| Comic Sans MS | 0.0% | 95 |
| Brush Script MT | 5.9% | 67 |
| Snell Roundhand | 20.6% | 55 |
Comic Sans is informal and perfectly legible to the engine, because its letters are separate shapes of consistent width. Snell Roundhand is a connected script, and a fifth of the characters came back wrong.
That is the real distinction. Tesseract's model was trained on printed type and expects to cut a line into discrete glyphs. Connected writing has no clean cut points, so the segmentation fails before the recognition gets a chance. Genuine handwriting is worse than Snell Roundhand, not better, because it also varies letter to letter.
If you need handwriting read, this is the wrong tool and so is every other Tesseract front end. That job needs a model trained on handwriting.
Which characters does it get wrong?
Letters that look like digits, and the direction is consistent.
I rendered a line of the usual suspects and read it back:
truth: O0 l1I 5S 8B 2Z rn/m
read: 00 111 5S 8B 2Z rn/m
Capital O became a zero. Lowercase l and capital I both became the digit 1. The pairs people worry about most, 5 against S and 8 against B, came back correct, as did rn against m.
The pattern is that an isolated letter with a digit-shaped glyph is read as the digit. In running prose this rarely matters, because the language model around it pulls the result back to the word. In a reference code, a serial number or a licence plate, there is no surrounding word to help, and that is exactly where this kind of tool gets used.
Proofread any string that mixes letters and digits with no spaces. It is the one class of output where the engine is confidently wrong.
What does the confidence score tell you?
Roughly how the page went, and not whether the number you care about is right.
The tool shows the engine's mean word confidence as high, medium or low, with high at 85 and above. Against the measured error rates, that mapping is broadly sensible and optimistic at the edge:
| Case | Confidence | Label | Real error rate |
|---|---|---|---|
| Clean | 95 | high | 0.0% |
| 5 degrees of skew | 91 | high | 2.9% |
| 12 degrees of skew | 70 | medium | 16.7% |
| 320 px wide | 41 | low | 85.3% |
| Connected script | 55 | low | 20.6% |
A low score is a reliable warning: everything that scored under 60 was genuinely broken. A high score is weaker evidence than it looks. The 5 degree case scored 91, which the interface calls high, while quietly mangling a word in an invoice line.
Use it as a smoke alarm rather than a guarantee. Low means do not bother reading the output. High means the page as a whole went well, which is not the same as every character being right.
How much does each language cost to download?
Between 0.67 and 2.82 megabytes, once, and then it is cached.
The engine needs a trained data file per language. Because the tool runs the LSTM model only, it pulls the integerised best-quality set, which is considerably smaller than the files most OCR write-ups quote. Measured from the CDN the library defaults to:
| Language | Download | Language | Download |
|---|---|---|---|
| English | 2.82 MB | Hindi | 1.32 MB |
| Russian | 2.56 MB | Bengali | 1.31 MB |
| Spanish | 2.00 MB | Korean | 1.50 MB |
| Japanese | 1.94 MB | German | 1.27 MB |
| Chinese (Simplified) | 1.64 MB | Portuguese | 1.33 MB |
| Arabic | 1.58 MB | Italian | 1.58 MB |
| French | 0.67 MB |
All thirteen together come to about 21 MB, which is less than one photograph from a modern phone. The common assumption that Chinese or Japanese must be enormous does not hold here: Chinese Simplified is 1.64 MB, smaller than English, because the file stores a model rather than a glyph library.
Why is the first run slower than the second?
Because the first one is downloading and starting an engine, and the second one is just reading.
The first run of a language fetches the WebAssembly build of Tesseract, fetches that language's data file, and initialises a worker. After that the worker stays alive in the page and the data file is cached by the browser, so a second image in the same language starts immediately. Switching languages re-initialises the worker but does not re-download anything already fetched.
The recognition itself is quick. On this machine, reading the three line test image took 186 milliseconds for the clean version and between 70 and 125 milliseconds for the damaged ones, all on an already-running worker. For a full page of text expect a second or two rather than a tenth of one, but the first-run wait you notice is setup, not reading.
Once a language is cached the tool works with no network at all, which is a genuine offline capability rather than a marketing line.
What should you do to an image before running OCR?
Three things, in this order, and the first one matters more than the other two combined.
- Straighten it. Rotate until the lines of text are level. Three degrees is free, five is not.
- Crop to the text. This fixes the column problem, removes background the segmenter has to reject, and makes the text a larger share of the frame.
- Leave everything else alone. Do not sharpen, do not raise the contrast, do not convert to black and white by hand. The engine binarises anyway, and a sharpening filter adds ringing around the letter edges that was not there before.
The thing worth adding rather than removing is resolution, but only if you are near the floor. Going from a 7 pixel capital height to a 14 pixel one changed nothing in the measurements. Going from 6 to 7 changed everything.
When is OCR the wrong tool?
When the picture is not of text, when the text is handwritten, and when the thing you want is a searchable document rather than a block of characters.
OCR gives you the words with no structure: no headings, no table cells, no paragraph boundaries you can trust, no reading order across a complex layout. For a quote out of a screenshot or an address off a photo that is exactly right. For converting a scanned report into something editable it is the first of several steps.
If what you want is a description of a picture rather than the words printed in it, that is a different model entirely and a different tool. And if the source is a PDF that was generated rather than scanned, the text is already in the file: pulling it out directly keeps the layout and loses nothing, which no amount of image recognition can match.
Why does this run in the browser?
Because the pictures people put through OCR are the ones they would least like to upload.
Receipts, bank letters, prescriptions, passports, ID cards, a photo of a contract, a screenshot of a private conversation. The usual alternative is a site that takes your image, processes it on a server you know nothing about, and keeps whatever it keeps.
Here the engine is WebAssembly running in the page, in a worker. The image is decoded into tab memory, read there, and the text appears. Nothing is uploaded, there is no queue and no stored copy, and after the first run the whole thing works offline. The costs are the one-off language download and that very large images are limited by what the tab can hold.
Two things this site does not yet do, and which belong in the same pipeline: straightening a photographed page and cropping it to the text. Both have to happen in whatever viewer you already have, which is the one awkward seam in the workflow above. What comes after is covered: if the source is a photo of several pages, the guide on combining images deals with putting the results back together.



