# Tesseract.js User Guide

Tesseract.js is a pure JavaScript optical character recognition (OCR) library that extracts text from images in [almost any language](#supported-languages). It runs in the browser and in Node.js with no native binaries to install — recognition is performed by a WebAssembly port of the [Tesseract](https://github.com/tesseract-ocr/tesseract) OCR engine that the library downloads and manages for you.

This guide is for developers who want to *use* Tesseract.js in an application: reading text from images, controlling the recognition output, running jobs in parallel, and troubleshooting common problems. For architecture, build internals, and deployment concerns, see the Admin Guide.

> [!NOTE]
> This guide documents **Tesseract.js v7.0.0**. The npm package is `tesseract.js`. The library is licensed under Apache-2.0.

---

## Table of Contents

1. [Quick Start](#quick-start)
2. [Installation](#installation)
3. [Core Concepts](#core-concepts)
4. [Everyday Usage](#everyday-usage)
5. [Choosing Output Formats](#choosing-output-formats)
6. [Working With Languages](#working-with-languages)
7. [Running Jobs in Parallel With Schedulers](#running-jobs-in-parallel-with-schedulers)
8. [Common Tasks (Recipes)](#common-tasks-recipes)
9. [Performance Best Practices](#performance-best-practices)
10. [How It Works](#how-it-works)
11. [Troubleshooting](#troubleshooting)
12. [FAQ](#faq)
13. [API Reference](#api-reference)

---

## Quick Start

Recognize English text from an image URL in three lines of real work:

=== "Browser (ESM / bundler)"

    ```javascript
    import { createWorker } from 'tesseract.js';

    (async () => {
      const worker = await createWorker('eng');
      const ret = await worker.recognize('https://tesseract.projectnaptha.com/img/eng_bw.png');
      console.log(ret.data.text);
      await worker.terminate();
    })();
    ```

=== "Browser (script tag / CDN)"

    ```html
    <script src="https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js"></script>
    <script>
      (async () => {
        const worker = await Tesseract.createWorker('eng');
        const ret = await worker.recognize('https://tesseract.projectnaptha.com/img/eng_bw.png');
        console.log(ret.data.text);
        await worker.terminate();
      })();
    </script>
    ```

=== "Node.js"

    ```javascript
    const { createWorker } = require('tesseract.js');

    (async () => {
      const worker = await createWorker('eng');
      const { data: { text } } = await worker.recognize('./image.png');
      console.log(text);
      await worker.terminate();
    })();
    ```

Expected output (for the sample image):

```text
Mild Splendour of the various-vesfed Night!
...
```

> [!TIP]
> When recognizing multiple images, create the worker **once**, call `worker.recognize` for each image, and call `worker.terminate()` once at the end. Do not create and destroy a worker per image — see [Performance Best Practices](#performance-best-practices).

---

## Installation

Tesseract.js works three ways: a `<script>` tag (local copy or CDN), a bundler such as webpack via npm, and Node.js via npm or yarn.

### CDN (browser, no build step)

```html
<!-- v5 -->
<script src='https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js'></script>
```

After the script loads, a global `Tesseract` variable is available and you can create a worker with `Tesseract.createWorker`.

An ESM build (for `import` syntax) is available at:

```text
https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.esm.min.js
```

### npm / yarn (bundlers and Node.js)

```shell
# Latest version
npm install tesseract.js
# or
yarn add tesseract.js
```

```shell
# A specific (older) version
npm install tesseract.js@3.0.3
# or
yarn add tesseract.js@3.0.3
```

Expected output:

```text
added 1 package, and audited N packages in Xs
```

> [!IMPORTANT]
> **Tesseract.js v7 requires Node.js v16 or newer.** (Tesseract.js v6 requires Node.js v14 or newer.) Installing on an unsupported Node version can fail outright — see [Troubleshooting](#error-new-installations-npm-install-broken-on-nodejs-v14).

### Requirements at a glance

| Requirement | Detail |
|---|---|
| Node.js (v7) | v16 or newer |
| Browser | Any browser that supports JavaScript **and** WebAssembly |
| Network (default) | Outbound HTTPS to the jsDelivr CDN for the WASM core and language data |
| Disk / storage | Space to cache `.traineddata` files (browser: IndexedDB; Node.js: filesystem) |

> [!NOTE]
> Tesseract.js downloads its WebAssembly core and language data at runtime from a CDN by default. If your environment cannot reach the CDN, host these files yourself and set `corePath` and `langPath` — see [Self-hosting the core and language data](#recipe-self-host-the-core-and-language-data).

---

## Core Concepts

Understanding four terms makes the rest of the API obvious.

**Worker**
: An object that owns one instance of the Tesseract engine, running in a web worker (browser) or a worker thread (Node.js). You send OCR jobs through the worker. Created with `createWorker`.

**Scheduler**
: A manager that holds a pool of workers and a job queue, dispatching jobs to idle workers so they run in parallel. Created with `createScheduler`. Use it when you have many images to process. See [Workers vs. Schedulers](#running-jobs-in-parallel-with-schedulers).

**OEM (OCR Engine Mode)**
: Selects which recognition model runs. Tesseract.js defaults to **LSTM only** (the modern neural model). See [OEM](#oem).

**PSM (Page Segmentation Mode)**
: Tells the engine how the text is laid out on the page (a single line, a single block, sparse text, etc.). See [PSM](#psm).

> [!NOTE]
> Tesseract.js is a *wrapper*. It brings the Tesseract engine to JavaScript but does not change how recognition works. The recognition model and most engine parameters come from the upstream [Tesseract project](https://github.com/tesseract-ocr/tesseract). This guide documents the JavaScript API and the options Tesseract.js adds; for deep recognition-tuning questions, the upstream Tesseract documentation is authoritative.

---

## Everyday Usage

### Create a worker

```javascript
const worker = await createWorker('eng');
```

`createWorker(langs, oem, options, config)` — all arguments are optional:

| Argument | Default | Meaning |
|---|---|---|
| `langs` | `'eng'` | Language code, `+`-joined string (`'eng+chi_sim'`), or array (`['eng', 'chi_sim']`) |
| `oem` | `1` (`OEM.LSTM_ONLY`) | OCR engine mode |
| `options` | `{}` | Worker options (paths, caching, logger, etc.) — see [WorkerOptions](#workeroptions) |
| `config` | `{}` | "Init-only" Tesseract parameters set before initialization |

> [!IMPORTANT]
> `createWorker` is **asynchronous** — always `await` it. The returned worker is already loaded and initialized with the requested language and OEM; you do **not** need to call `load`, `loadLanguage`, or `initialize` (those were removed in v5).

### Recognize an image

```javascript
const { data: { text } } = await worker.recognize(image);
console.log(text);
```

`worker.recognize(image, options, output, jobId)` returns a promise to an object with `jobId` and `data` properties. The `data` object contains every output format you requested (by default, just `text`).

> [!TIP]
> Recognition quality scales with image resolution. The same image often gives much better results if you upscale it before calling `recognize`.

> [!NOTE]
> `worker.recognize` returns a result object **even when no text is found** (the outputs are simply empty). An empty page is treated as a valid result, not an error, so no exception is thrown.

### Track progress with a logger

Pass a `logger` function in the worker options to receive progress updates:

```javascript
const worker = await createWorker('eng', 1, {
  logger: (m) => console.log(m), // e.g. { status: 'recognizing text', progress: 0.5, ... }
});
```

A logger message looks like:

```text
{ workerId: 'Worker-0-abc12', jobId: 'Job-0-de34f', status: 'recognizing text', progress: 0.5 }
```

### Always terminate

```javascript
await worker.terminate();
```

Terminating frees the worker's memory and underlying thread. For a single one-off recognition you can terminate immediately; when reusing a worker, terminate only after all jobs are done.

---

## Choosing Output Formats

By default, `worker.recognize` returns only plain `text`. To get other formats, pass an `output` object as the **third** argument with the formats you want set to `true`.

```javascript
const { data } = await worker.recognize(image, {}, {
  text: true,
  hocr: true,
  tsv: true,
  blocks: true,
});
console.log(data.hocr); // hOCR (HTML) string
```

> [!WARNING]
> As of **v6**, every output format **other than `text` is disabled by default**. If you previously relied on `hocr`, `blocks`, etc. being present, you must now explicitly enable them: `worker.recognize(image, {}, { hocr: true })`.

### Available output formats

| Output key | Type returned | Description |
|---|---|---|
| `text` | `string` | Plain UTF-8 recognized text (default `true`) |
| `blocks` | `object` | Structured JSON tree of blocks → paragraphs → lines → words → symbols |
| `layoutBlocks` | `object` | Layout-only blocks (does not require full recognition) |
| `hocr` | `string` | hOCR — HTML representation with bounding boxes |
| `tsv` | `string` | Tab-separated values with positional data |
| `box` | `string` | Box file (character coordinates) |
| `unlv` | `string` | UNLV format |
| `osd` | `string` | Orientation & script detection text |
| `pdf` | `number[]` | Searchable PDF as a byte array |
| `imageColor` | `string` | Pre-processed color image (base64 data URL) |
| `imageGrey` | `string` | Pre-processed greyscale image |
| `imageBinary` | `string` | Pre-processed binary (black/white) image |
| `debug` | `string` | Internal debug output |

The full recognition result (`data`) also always includes `confidence`, `psm`, `oem`, and `version`.

> [!TIP]
> `imageColor`, `imageGrey`, `imageBinary`, `layoutBlocks`, and `debug` do **not** require running full text recognition. If you request *only* these, Tesseract.js skips the OCR step, which is faster. See [Recipe: extract pre-processed images](#recipe-extract-pre-processed-images).

> [!CAUTION]
> Tesseract.js can **produce** searchable PDFs via the `pdf` output, but it cannot **read** PDF files as input. See [Are PDF files supported?](#q-are-pdf-files-supported).

---

## Working With Languages

Specify one or more languages when you create the worker. Language data (`.traineddata`) is downloaded automatically the first time it is needed and then cached.

```javascript
// Single language
const worker = await createWorker('eng');

// Multiple languages, '+'-joined
const worker = await createWorker('eng+chi_tra');

// Multiple languages as an array
const worker = await createWorker(['eng', 'chi_tra']);
```

> [!NOTE]
> The `languages` constant exports symbolic names for every supported language code (for example `Tesseract.languages.ENG === 'eng'`, `Tesseract.languages.CHI_SIM === 'chi_sim'`). You can pass either the code string directly or the constant.

### Supported languages

Tesseract.js supports the full set of Tesseract language codes — roughly 90 languages including English (`eng`), Chinese Simplified (`chi_sim`) and Traditional (`chi_tra`), Japanese (`jpn`), Korean (`kor`), Arabic (`ara`), Hindi (`hin`), Russian (`rus`), German (`deu`), French (`fra`), Spanish (`spa`), and many more. The `osd` "language" is used for orientation/script detection only.

For the complete, current list of codes see the upstream [Tesseract data files documentation](https://tesseract-ocr.github.io/tessdoc/Data-Files#data-files-for-version-400-november-29-2016).

> [!TIP]
> When you need the Latin-script default, `'eng'` is the right choice. Combining many heavy languages (for example Chinese with the Legacy model) downloads large data files — see [Do Not Disable Language Data Caching](#performance-best-practices).

---

## Running Jobs in Parallel With Schedulers

There are two ways to run recognition: a single worker (simple), or a **scheduler** that spreads jobs across multiple workers (parallel, much faster for bulk work).

### When to use which

| Situation | Use |
|---|---|
| One image, or images processed one after another | A single reused worker |
| Many images that can be processed at once | A scheduler with a pool of workers |

> [!CAUTION]
> Do **not** create a new worker for every image when processing in parallel. Each worker uses a large amount of memory, so spawning an unbounded number of workers can crash your application. Use a scheduler with a fixed pool instead.

### Scheduler example

```javascript
const { createWorker, createScheduler } = require('tesseract.js');

const scheduler = createScheduler();

const workerGen = async () => {
  const worker = await createWorker('eng');
  scheduler.addWorker(worker);
};

const workerN = 4; // pool of 4 workers
(async () => {
  await Promise.all(Array(workerN).fill(0).map(() => workerGen()));

  // Run 10 recognition jobs across the 4 workers
  const results = await Promise.all(
    Array(10).fill(0).map(() => (
      scheduler.addJob('recognize', 'https://tesseract.projectnaptha.com/img/eng_bw.png')
        .then((x) => x.data.text)
    )),
  );

  console.log(results);
  await scheduler.terminate(); // terminates all workers too
})();
```

> [!IMPORTANT]
> All workers added to the same scheduler should be **homogeneous** — same language and same parameters. The scheduler assigns jobs to workers non-deterministically, so mismatched workers would produce results that depend on which worker happened to take the job.

`scheduler.addJob(action, ...payload)` accepts `'recognize'` or `'detect'` as the action, followed by the same arguments you would pass to `worker.recognize` / `worker.detect`.

---

## Common Tasks (Recipes)

### Recipe: recognize only digits (character whitelist)

```javascript
const worker = await createWorker('eng');
await worker.setParameters({
  tessedit_char_whitelist: '0123456789',
});
const { data: { text } } = await worker.recognize(image);
await worker.terminate();
```

> [!TIP]
> A character whitelist dramatically improves accuracy when you know the content is constrained (invoice totals, license plates, serial numbers).

### Recipe: choose a page segmentation mode

```javascript
const { createWorker, PSM } = require('tesseract.js');

const worker = await createWorker('eng');
await worker.setParameters({
  tessedit_pageseg_mode: PSM.SINGLE_BLOCK,
});
const { data: { text } } = await worker.recognize(image);
```

### Recipe: recognize only part of an image (rectangle)

```javascript
const worker = await createWorker('eng');
const rectangle = { left: 0, top: 0, width: 500, height: 250 };
const { data: { text } } = await worker.recognize(image, { rectangle });
```

To read several regions, call `recognize` once per rectangle (optionally via a scheduler to do them in parallel):

```javascript
const rectangles = [
  { left: 0,   top: 0, width: 500, height: 250 },
  { left: 500, top: 0, width: 500, height: 250 },
];
const values = [];
for (let i = 0; i < rectangles.length; i += 1) {
  const { data: { text } } = await worker.recognize(image, { rectangle: rectangles[i] });
  values.push(text);
}
```

### Recipe: auto-rotate skewed scans

```javascript
const { data: { text, rotateRadians } } = await worker.recognize(
  image,
  { rotateAuto: true },
);
console.log('Rotated by (radians):', rotateRadians);
```

> [!NOTE]
> With `rotateAuto: true`, Tesseract.js detects the skew angle and re-runs recognition on the corrected image. Very small angles (under ~0.3°) are ignored to save runtime. You can also set a fixed angle with `rotateRadians`.

### Recipe: generate a searchable PDF (Node.js)

```javascript
const fs = require('fs');
const { createWorker } = require('tesseract.js');

(async () => {
  const worker = await createWorker();
  const { data: { pdf } } = await worker.recognize(
    'image.png',
    { pdfTitle: 'Example PDF' },
    { pdf: true },
  );
  fs.writeFileSync('tesseract-ocr-result.pdf', Buffer.from(pdf));
  await worker.terminate();
})();
```

Use `pdfTextOnly: true` in the options to produce a PDF containing only the text layer (no background image).

### Recipe: extract pre-processed images

```javascript
const { data: { imageColor, imageGrey, imageBinary } } = await worker.recognize(
  image,
  { rotateAuto: true },
  { imageColor: true, imageGrey: true, imageBinary: true },
);
```

Each is returned as a base64 data-URL string. In Node.js, convert and save:

```javascript
const convertImage = (src) => new Uint8Array(
  atob(src.split(',')[1]).split('').map((c) => c.charCodeAt(0)),
);
fs.writeFileSync('imageBinary.png', convertImage(imageBinary));
```

### Recipe: detect orientation and script (OSD)

```javascript
const worker = await createWorker('eng', 1, { legacyCore: true, legacyLang: true });
const { data } = await worker.detect(image);
console.log(data);
// { tesseract_script_id, script, script_confidence, orientation_degrees, orientation_confidence }
```

> [!WARNING]
> `worker.detect` requires the **Tesseract Legacy** model, which is not loaded by default. You must set both `legacyCore: true` and `legacyLang: true` in `createWorker` options, or `detect` will throw `` `worker.detect` requires Legacy model, which was not loaded. ``

### Recipe: self-host the core and language data

```javascript
const worker = await createWorker('eng', 1, {
  workerPath: 'https://your-host/tesseract.js@v5.0.0/dist/worker.min.js',
  langPath:   'https://your-host/tessdata/4.0.0',
  corePath:   'https://your-host/tesseract.js-core@v5.0.0',
});
```

> [!IMPORTANT]
> When you set `corePath`, point it at a **directory** that contains all four core files (`tesseract-core.wasm.js`, `tesseract-core-simd.wasm.js`, `tesseract-core-lstm.wasm.js`, `tesseract-core-simd-lstm.wasm.js`). Tesseract.js picks the right one for the user's device. Setting `corePath` to a single `.js` file is strongly discouraged — it degrades performance or breaks on some devices.

### Recipe: switch language/model on an existing worker

```javascript
// Worker created with the legacy code/data available:
const worker = await createWorker('eng', 1, { legacyCore: true, legacyLang: true });
await worker.reinitialize('eng', 0); // switch to Tesseract Legacy (oem 0)
```

> [!NOTE]
> Switching from LSTM (`oem` 1) to Legacy (`oem` 0) with `reinitialize` only works if the worker was created with `legacyCore: true` and `legacyLang: true`. Otherwise the required code/data is missing and `reinitialize` throws `Legacy model requested but code missing.`

---

## Performance Best Practices

> [!TIP]
> Always benchmark your own images. Some tips below are universal; others (changing language data or model) trade quality for speed.

- [ ] **Reuse workers.** Never create/load/destroy a worker per image. Reuse one worker for sequential jobs, or a scheduler pool for parallel jobs.
- [ ] **Set up workers ahead of time.** Create the worker before the user picks an image (for example on page load, or when the user signals intent to use OCR), so the first recognition isn't slowed by setup.
- [ ] **Do not disable language-data caching.** Leave `cacheMethod` at its default (`write`). Caching bugs that prompted some users to disable it were fixed in v4.0.6.
- [ ] **Use the latest version.** Old versions are much slower — v2 takes ~10× longer on some images than current.
- [ ] **Never set `corePath` to a single `.js` file.** Point it at a directory so the best build can be selected.
- [ ] **Consider "fast" language data** for speed-sensitive work by setting `langPath` to `https://tessdata.projectnaptha.com/4.0.0_fast` (benchmark quality impact first).

> [!NOTE]
> To experience first-time-user setup cost during development (no cache), set `cacheMethod: 'none'`. **Remove this before publishing your app** — it forces a re-download of language data on every run.

> [!IMPORTANT]
> In long-running Node.js servers, periodically recreate workers/schedulers (for example every 500 jobs). WebAssembly memory only grows over time, and workers accumulate words into an internal dictionary that can become bloated with typos across thousands of unrelated documents.

---

## How It Works

You can use Tesseract.js productively without this section, but it explains the moving parts behind the API.

```text
   Your code
      │  createWorker('eng')
      ▼
 ┌─────────────────────────┐        spawns        ┌──────────────────────────────┐
 │  Worker (main thread)    │ ───────────────────▶ │  Worker script               │
 │  src/createWorker.js     │   postMessage(...)   │  (web worker / worker_thread)│
 │  - recognize/detect/...  │ ◀─────────────────── │  src/worker-script/index.js  │
 └─────────────────────────┘   progress/result    │   ├─ loads TesseractCore WASM │
                                                   │   ├─ downloads .traineddata   │
                                                   │   └─ runs OCR                 │
                                                   └──────────────────────────────┘
```

1. **You call `createWorker`.** The main-thread API (`src/createWorker.js`) spawns a separate execution context — a `Worker` in the browser or a `worker_threads` `Worker` in Node.js — so recognition never blocks your UI or event loop.
2. **The worker script loads the engine.** `src/worker-script/index.js` asks an environment-specific adapter (`browser` or `node`) to load the right `TesseractCore` WebAssembly build. The build is chosen by detecting SIMD / relaxed-SIMD support and whether the Legacy model is needed.
3. **Language data is fetched and cached.** On `loadLanguage`, the worker checks the cache (browser: IndexedDB via `idb-keyval`; Node.js: the filesystem). If the `.traineddata` is missing, it downloads `.traineddata.gz` from the CDN, gunzips it, writes it into the WebAssembly in-memory filesystem (MEMFS), and stores it in the cache for next time.
4. **Recognition runs.** `worker.recognize` loads the image into the engine, optionally auto-rotates it, runs `Recognize`, and dumps the requested output formats into a JSON result that is posted back to the main thread.
5. **You terminate.** `worker.terminate` tears down the worker and frees memory.

> [!NOTE]
> Communication between your code and the worker is entirely message-based (`postMessage`). Every method on a worker — `recognize`, `detect`, `setParameters`, etc. — is a job sent into the worker and a promise resolved when the worker replies.

---

## Troubleshooting

Follow the format: error → cause → solution.

### Error: `Cannot find module`

**Cause:** The main Tesseract.js thread cannot locate the worker code. This commonly happens inside frameworks whose build systems move files in ways that violate Tesseract.js's assumptions about file locations.

**Solution:** Set `workerPath` explicitly to point at the local worker script.

```javascript
const worker = await createWorker('eng', 1, {
  workerPath: './node_modules/tesseract.js/src/worker-script/node/index.js', // Node.js
  // or, in the browser, the path to worker.min.js
});
```

**Prevention:** Confirm the official examples run first; if they do but your framework build does not, a `workerPath` override is almost always the fix.

### Error: `Error attempting to read image.`

**Cause:** The supplied image is not a supported format *and* data type combination, or the data is corrupt. Tesseract.js supports the image **formats** bmp, jpg, png, pbm, webp, and non-animated gif — but the data must also be a supported **type** (see [Supported image inputs](#supported-image-inputs)). For example, a buffer of raw pixel data (not an encoded image) is not supported.

**Solution:** Ensure you are passing an encoded image in a supported format and a supported data type. Re-encode raw pixels to PNG/JPEG first.

**Prevention:** Validate the source before recognition; for canvases use `toBlob`/`convertToBlob` rather than raw pixel arrays.

### Error: `initialization failed`

**Cause:** Initialization of the Tesseract API failed — most often because the cached `.traineddata` does not support the requested model (for example, requesting the Legacy model with LSTM-only data).

**Solution:** Tesseract.js automatically deletes the suspect cache entry and retries. If it still fails, ensure the OEM you request matches the data you load — request the Legacy model only with `legacyCore: true` and `legacyLang: true`, or simply use the default LSTM model.

**Prevention:** Don't mix Legacy `oem` values (`0`/`2`) with LSTM-only data.

### Error: `worker.detect requires Legacy model, which was not loaded.`

**Cause:** `worker.detect` (OSD) needs the Tesseract Legacy model, which is not loaded by default.

**Solution:** Create the worker with `legacyCore: true` and `legacyLang: true`.

**Prevention:** Reserve `detect` for workers explicitly configured with the Legacy model.

### Error: `Legacy model requested but code missing.`

**Cause:** You called `reinitialize` requesting a Legacy `oem` (`0` or `2`) on a worker created without the Legacy core.

**Solution:** Create the worker with `legacyCore: true` (and `legacyLang: true`) before switching to a Legacy `oem`.

### Error: `Network error while fetching <url>. Response code: <status>`

**Cause:** Downloading language data from `langPath`/the CDN failed (offline, blocked, wrong path, or 404).

**Solution:** Verify network access to the CDN. If self-hosting, confirm `langPath` points at a directory and the `<lang>.traineddata(.gz)` file exists there. Remember: do not include a trailing `/` on `langPath`.

**Prevention:** For air-gapped or restricted environments, self-host the data — see [Recipe: self-host the core and language data](#recipe-self-host-the-core-and-language-data).

### Warning: `Invalid resolution 0 dpi. Using 70 instead.`

**Cause:** The image has no DPI metadata.

**Solution:** Set a DPI explicitly:

```javascript
await worker.setParameters({ user_defined_dpi: '300' });
```

### New installations (`npm install`) broken on Node.js v14

**Cause:** Tesseract.js v7 requires Node.js **v16+**; v6 requires v14+. Installing a version newer than your Node supports can break the install.

**Solution:** Upgrade Node.js, or pin Tesseract.js to a version compatible with your Node (for example use v6 on Node 14).

### Poor accuracy on angled or rotated text

**Cause:** Skewed text reduces recognition quality.

**Solution:** Enable `rotateAuto: true` (see [Recipe: auto-rotate skewed scans](#recipe-auto-rotate-skewed-scans)) and ensure the image is high resolution. Recognition of severely rotated or distorted text remains a known limitation of the underlying engine.

---

## FAQ

**Q: What is the scope of this project?**
Tesseract.js is the JavaScript/WebAssembly port of the Tesseract OCR engine. It does not modify the underlying recognition engine. Bugs in recognition behavior originate upstream; if you can reproduce a recognition bug in the [main CLI Tesseract](https://github.com/tesseract-ocr/tesseract), report it there.

**Q: Which JavaScript frameworks are supported?**
All frameworks that support JavaScript and WebAssembly. The one common framework known **not** to work is **React Native**, because it does not support WebAssembly.

<a name="q-are-pdf-files-supported"></a>
**Q: Are PDF files supported?**
No — Tesseract.js cannot take a PDF as input. To OCR a PDF you must render its pages to images first (with a library such as [PDF.js](https://github.com/mozilla/pdf.js/) or [muPDF](https://github.com/ArtifexSoftware/mupdf)) and recognize those images. Alternatively, [Scribe.js](https://github.com/scribeocr/scribe.js) builds on Tesseract.js and adds native PDF support. (Note: Tesseract.js *can produce* searchable PDFs as **output** — see [Recipe: generate a searchable PDF](#recipe-generate-a-searchable-pdf-nodejs).)

**Q: Is handwritten text supported?**
No. The Tesseract model assumes printed text. No combination of options meaningfully improves handwriting recognition unless the handwriting closely resembles print.

**Q: Why do I get different results than the Tesseract CLI?**
Usually because of different defaults. Tesseract.js defaults to `oem` `1` (LSTM only) while the CLI defaults to `2` (LSTM + Legacy fallback); Tesseract.js defaults to `psm` `6` (single block) while the CLI defaults to `3` (auto). Set `oem` and `psm` explicitly in both, confirm identical language data and Tesseract version, and results should match.

**Q: What configuration settings should I use?**
The defaults are optimal for most users. The recognition engine and its many parameters come from upstream Tesseract, so questions like "what settings work best for license plates" are usually answered in the [main Tesseract documentation](https://github.com/tesseract-ocr/tesseract), not here.

**Q: How does Tesseract.js download and keep `*.traineddata`?**
On first use it checks the cache (browser IndexedDB; Node.js filesystem). If the file is missing it fetches `*.traineddata.gz` from the CDN, gunzips it, and stores it. Delete the cached file to force a re-download.

**Q: How can I train my own `*.traineddata`?**
Follow the upstream [Tesseract training documentation](https://tesseract-ocr.github.io/tessdoc/). Custom traineddata can then be loaded via `langPath`.

**Q: Can I run OCR completely offline?**
Yes — self-host the WASM core (`corePath`), worker script (`workerPath`), and language data (`langPath`). See [Recipe: self-host the core and language data](#recipe-self-host-the-core-and-language-data).

**Q: Does Tesseract.js work in Web Workers / Cloudflare Workers / browser extensions?**
It runs in standard browsers and Node.js. Some constrained or non-standard runtimes (certain serverless worker platforms, some browser-extension manifest configurations) may need extra path configuration or may be unsupported; check the project's issue tracker for the current status of your specific runtime.

---

## API Reference

### Top-level exports

The package exports: `createWorker`, `createScheduler`, `recognize`, `detect`, `setLogging`, `OEM`, `PSM`, `languages`, plus the `Tesseract` namespace types.

| Export | Kind | Purpose |
|---|---|---|
| `createWorker(langs?, oem?, options?, config?)` | function | Create and initialize a worker (async) |
| `createScheduler()` | function | Create a scheduler that pools workers |
| `recognize(image, langs?, options?)` | function | ⚠️ Deprecated — one-shot recognize (creates + destroys a worker each call) |
| `detect(image, options?)` | function | ⚠️ Deprecated — one-shot detect |
| `setLogging(logging)` | function | Globally enable/disable verbose logging |
| `OEM` | enum | OCR Engine Mode constants |
| `PSM` | enum | Page Segmentation Mode constants |
| `languages` | object | Map of symbolic names → language codes |

> [!WARNING]
> The top-level `recognize()` and `detect()` functions are **deprecated**. They create, load, and destroy a worker on every call, which is slow. Use `worker.recognize` / `worker.detect` instead.

### Worker methods

| Method | Returns | Purpose |
|---|---|---|
| `recognize(image, options?, output?, jobId?)` | `Promise<RecognizeResult>` | Run OCR on an image |
| `detect(image, jobId?)` | `Promise<DetectResult>` | Orientation & script detection (needs Legacy model) |
| `setParameters(params, jobId?)` | `Promise<ConfigResult>` | Set Tesseract parameters at runtime |
| `reinitialize(langs?, oem?, config?, jobId?)` | `Promise<ConfigResult>` | Re-initialize with different language/model |
| `writeText(path, text, jobId?)` | `Promise<ConfigResult>` | Write a text file into the worker's in-memory FS |
| `readText(path, jobId?)` | `Promise<ConfigResult>` | Read a file from the in-memory FS |
| `removeFile(path, jobId?)` | `Promise<ConfigResult>` | Delete a file from the in-memory FS |
| `FS(method, args, jobId?)` | `Promise<ConfigResult>` | Call any [Emscripten FS](https://emscripten.org/docs/api_reference/Filesystem-API.html) method |
| `terminate(jobId?)` | `Promise<ConfigResult>` | Terminate the worker and free memory |

> [!NOTE]
> The optional `jobId` argument lets you label a job so you can correlate it in logger callbacks. If omitted, an id is generated automatically.

> [!NOTE]
> `worker.setParameters` cannot change the `oem` — that is fixed at initialization. To change the model on an existing worker, use `reinitialize`. Parameters flagged "init only" in Tesseract docs likewise cannot be set via `setParameters` or `recognize`; pass them through the `config` argument of `createWorker`/`reinitialize`.

### Scheduler methods

| Method | Returns | Purpose |
|---|---|---|
| `addWorker(worker)` | `string` | Add a worker to the pool; returns the worker id |
| `addJob(action, ...payload)` | `Promise` | Queue a `'recognize'` or `'detect'` job |
| `getQueueLen()` | `number` | Current job-queue length |
| `getNumWorkers()` | `number` | Number of workers in the pool |
| `terminate()` | `Promise` | Terminate all workers and clear the queue |

> [!IMPORTANT]
> `scheduler.addJob` throws `You need to have at least one worker before adding jobs` if the pool is empty. Add workers before queueing jobs.

### `recognize` options (second argument)

| Option | Type | Description |
|---|---|---|
| `rectangle` | `{ left, top, width, height }` | Restrict recognition to a region |
| `rotateAuto` | `boolean` | Auto-detect and correct skew before recognizing |
| `rotateRadians` | `number` | Apply a fixed rotation (radians) |
| `pdfTitle` | `string` | Title embedded in `pdf` output |
| `pdfTextOnly` | `boolean` | Produce a text-only PDF (no background image) |

Any other key in this object that is not a Tesseract.js option (and does not start with `tessjs_`) is passed through to the Tesseract engine as a parameter for that single recognition call.

### WorkerOptions

Passed as the third argument to `createWorker`.

| Option | Type | Default | Description |
|---|---|---|---|
| `corePath` | string | jsDelivr CDN | Directory containing the four `tesseract.js-core` WASM builds |
| `langPath` | string | jsDelivr CDN | Directory for `.traineddata` downloads (no trailing `/`) |
| `workerPath` | string | jsDelivr CDN | Location of the worker script |
| `dataPath` | string | `.` | Path inside the WASM FS for traineddata (rarely changed) |
| `cachePath` | string | `.` | Cache location (Node.js folder; browser IndexedDB key prefix) |
| `cacheMethod` | string | `write` | Caching behavior — see below |
| `legacyCore` | boolean | `false` | Ensure downloaded code supports the Legacy model |
| `legacyLang` | boolean | `false` | Ensure downloaded language data supports the Legacy model |
| `workerBlobURL` | boolean | `true` | Use a Blob URL for the worker script (browser) |
| `gzip` | boolean | `true` | Whether remote traineddata is gzipped |
| `logger` | function | `() => {}` | Progress callback, e.g. `m => console.log(m)` |
| `errorHandler` | function | — | Worker error callback, e.g. `err => console.error(err)` |

**`cacheMethod` values:**

| Value | Reads cache? | Writes cache? |
|---|---|---|
| `write` (default) | Yes | Yes |
| `readOnly` | Yes | No |
| `refresh` | No | Yes |
| `none` | No | No |

### PSM

Page Segmentation Mode (`import { PSM } from 'tesseract.js'`). Tesseract.js defaults to `SINGLE_BLOCK`.

| Constant | Value | Meaning |
|---|---|---|
| `OSD_ONLY` | `'0'` | Orientation and script detection only |
| `AUTO_OSD` | `'1'` | Automatic page segmentation with OSD |
| `AUTO_ONLY` | `'2'` | Automatic, no OSD or OCR |
| `AUTO` | `'3'` | Fully automatic, no OSD (CLI default) |
| `SINGLE_COLUMN` | `'4'` | A single column of variable-size text |
| `SINGLE_BLOCK_VERT_TEXT` | `'5'` | A single uniform block of vertical text |
| `SINGLE_BLOCK` | `'6'` | A single uniform block of text (Tesseract.js default) |
| `SINGLE_LINE` | `'7'` | A single text line |
| `SINGLE_WORD` | `'8'` | A single word |
| `CIRCLE_WORD` | `'9'` | A single word in a circle |
| `SINGLE_CHAR` | `'10'` | A single character |
| `SPARSE_TEXT` | `'11'` | Sparse text — find as much text as possible, no order |
| `SPARSE_TEXT_OSD` | `'12'` | Sparse text with OSD |
| `RAW_LINE` | `'13'` | Raw line — treat as a single line, bypassing hacks |

### OEM

OCR Engine Mode (`import { OEM } from 'tesseract.js'`). Tesseract.js defaults to `LSTM_ONLY`.

| Constant | Value | Meaning |
|---|---|---|
| `TESSERACT_ONLY` | `0` | Legacy engine only |
| `LSTM_ONLY` | `1` | Neural (LSTM) engine only — **default** |
| `TESSERACT_LSTM_COMBINED` | `2` | Legacy + LSTM combined |
| `DEFAULT` | `3` | Whatever is available |

### Frequently used Tesseract parameters

Set via `worker.setParameters({...})` or per call in `recognize` options.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `tessedit_pageseg_mode` | enum (PSM) | `PSM.SINGLE_BLOCK` | Page segmentation mode |
| `tessedit_char_whitelist` | string | `''` | Restrict recognition to these characters |
| `tessedit_char_blacklist` | string | `''` | Forbid these characters |
| `preserve_interword_spaces` | string | `'0'` | `'1'` keeps spaces between words |
| `user_defined_dpi` | string | `''` | Set DPI to fix the "Invalid resolution 0 dpi" warning |

This is not exhaustive — because Tesseract.js passes parameters straight to the engine, any parameter supported by the underlying Tesseract version works here too (except "init only" parameters, which must go through `config`).

### Supported image inputs

**Image formats:** bmp, jpg, png, pbm, webp, gif (non-animated).

| Data type | Browser | Node.js |
|---|---|---|
| base64 data-URL string | ✅ | ✅ |
| `Buffer` / array buffer | ✅ | ✅ |
| `File` / `Blob` | ✅ | — |
| `img` / `canvas` / `video` element, `OffscreenCanvas` | ✅ | — |
| local file path string | — | ✅ |
| remote URL string | ✅ | ✅ |

> [!CAUTION]
> The image must be **both** a supported format **and** a supported data type. A buffer containing a PNG is supported; a buffer of raw pixel data is not.

---

_Generated June 2026 from naptha/tesseract.js at a1ca80d. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
