Scribe.js: A JavaScript OCR Library That Also Writes Searchable PDFs
JavaScript OCR and text extraction for images and PDFs. Projects Scribe OCR: officially supported GUI front-end for Scribe.js Site at scribeocr.com, repo at github.com/scribeocr/scribeocr If you have a project or example repo that uses Scribe.js, feel free to add it to this list using a pull request.
At a glance
- What is it?
- Scribe.js is an ESM-only JavaScript library for OCR and text extraction from images and PDFs, with a notable trick: it can insert an invisible text layer into existing PDFs. It is developer-focused, AGPL-3.0 licensed, and requires same-origin serving in the browser.
- Who is it for?
- Scribe.js is for developers who need OCR and PDF text extraction in a JavaScript environment and who value the ability to generate searchable PDFs with an invisible text layer. It is not for those who need a CDN-hosted script or a UMD build, nor for projects that cannot comply with AGPL-3.0.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 14, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What Scribe.js Solves and Who It Serves
Scribe.js addresses a specific gap in the JavaScript ecosystem: extracting text from images and PDFs, and also writing PDFs that contain a searchable text layer. It is a library, not an application. The README is explicit that it is intended for developers, and it points end users to the officially supported GUI at scribeocr.com. The common use cases are recognizing text from images, extracting text from user-uploaded PDFs (both text-native and image-native), and inserting an invisible text layer into an existing PDF to make it searchable. This last capability is what sets it apart from many other OCR tools, which typically only read text. The target audience is a developer building a web app or Node service that needs to process user documents, especially PDFs that may be scanned or image-based. If you are building a document management system, a receipt scanner, or a searchable archive, Scribe.js is aimed directly at you.
The Mechanism: ESM, openDocument, and the Text Layer
Scribe.js is written in JavaScript using ESM, which means it can be imported directly from browser or Node.js code without a build step. The core abstraction is the document object. You call scribe.openDocument with an array of file paths or URLs, and you get a document object with methods like recognize and download. The recognize method takes options such as { langs: ['eng'] } to specify OCR languages. The download method can output a PDF, and the README example shows that after recognizing text from an image, you can download a searchable PDF with an invisible text layer. This implies that the library does not just extract text; it also renders the recognized text into the PDF structure, positioning it invisibly over the original image or page. The README does not detail the internal data flow, but the existence of a download('pdf') method after recognize suggests that the OCR results are stored in a structured format that can be serialized into a PDF. The scribe.extractText function is a simpler wrapper that returns text directly, but the README warns it is not ideal for production use, implying it lacks the control and optimization of the document object API.
Getting It Running: Installation and First Commands
Installation is straightforward via npm: npm i scribe.js-ocr. The package name is scribe.js-ocr, not scribe.js, which is a detail to note. After installation, you import the default export: import scribe from 'scribe.js-ocr'. In a browser without a bundler, you can import directly from the node_modules path, but the README warns that all files must be served from the same origin as the importing file. That means no CDN imports. The simplest usage is the extractText function, which takes an array of sources, such as a URL to an image, and returns the recognized text. You must call await scribe.terminate() after all recognition is done so that a Node process can exit. For more control, you use the document object: const doc = await scribe.openDocument(['receipt.png']); await doc.recognize({ langs: ['eng'] }); await doc.download('pdf', 'receipt.pdf');. The recognize method accepts an options object with a langs array, which is the only configuration shown in the README. The library also has a CLI, documented in docs/cli.md, but the README does not show its usage.
The Same-Origin Constraint and No UMD Build
A significant limitation is that Scribe.js cannot be loaded from a CDN in the browser. The README states that all files must be served from the same origin as the file importing Scribe.js. This is a hard constraint that will affect deployment. If you are used to dropping a script tag for Tesseract.js from a CDN, Scribe.js will not work that way. There is no UMD version, so you cannot use it with a simple script tag. This means you must have a build step or serve the node_modules directory as static files. For a Node.js server, this is not an issue, but for a browser-only app, you need to ensure your server serves the library files from the same origin. This constraint could be a dealbreaker for projects that rely on CDN-hosted libraries for performance or simplicity. It also means that if you are building a browser extension or a local HTML file, you will need to handle the file serving yourself. The README does not explain why this constraint exists, but it is likely related to Web Workers or WASM loading, which often require same-origin policies.
When Scribe.js Is the Wrong Tool
Scribe.js is not the right choice if you need a quick, CDN-hosted script for a simple OCR task. The same-origin requirement and the lack of a UMD build add friction that Tesseract.js does not have. Also, if your project cannot comply with the AGPL-3.0 license, you should look elsewhere. AGPL-3.0 is a strong copyleft license, and the README does not offer a commercial license option. If you are building a proprietary application that you distribute, using Scribe.js could force you to release your source code under AGPL-3.0. That is a legal risk you need to evaluate with a lawyer. Additionally, the README notes that scribe.extractText is not ideal for production use, so you cannot rely on the simple API for a production system; you must invest in understanding the openDocument API. If your use case is only extracting text from text-native PDFs, you might not need OCR at all, and a simpler PDF parsing library could suffice. Scribe.js is a heavier dependency for that scenario.
Scribe.js vs. Tesseract.js: The Real Difference
The README includes a dedicated comparison document at docs/scribe_vs_tesseract.md, but the README itself does not summarize it. However, the core difference is clear from the feature set. Tesseract.js is a JavaScript port of the Tesseract OCR engine, and it focuses on recognizing text from images. Scribe.js does that too, but it also handles PDFs as first-class inputs and outputs. The ability to write a searchable PDF with an invisible text layer is not something Tesseract.js does out of the box. Tesseract.js typically requires you to use a separate library to generate a PDF, and that library may not create an invisible text layer. Also, Scribe.js is ESM-only, while Tesseract.js offers a UMD build that can be used from a CDN. So the choice boils down to whether you need PDF output and are willing to handle the same-origin constraint, versus wanting a simpler, CDN-friendly library. The comparison document likely goes into more detail, but the README does not provide it, so you would need to read that file in the repo.
Maintenance, License, and Upgrade Costs
The repository is active, with the latest release v0.12.0 pushed on 2026-05-27, and the default branch is master. The project is not archived, which suggests ongoing maintenance. However, the README does not provide a changelog or migration guide. When upgrading, you will need to check the release notes on GitHub. The license is AGPL-3.0, which has implications for how you can use the library in your own projects. If you modify the library or link it into a larger work, you may be required to distribute your source code under AGPL-3.0. This is a significant consideration for commercial or closed-source projects. The README does not offer a commercial license, so you must assume AGPL-3.0 applies. The project uses submodules for its repository, as indicated by the clone command with --recurse-submodules. That means if you contribute, you need to handle submodules. For consumers, the npm package should be self-contained, but the source repo has that complexity. The documentation is spread across several files, including a guide, API reference, and CLI reference, which you should budget time to read before integrating.
Editorial conclusion
Scribe.js is for developers who need OCR and PDF text extraction in a JavaScript environment and who value the ability to generate searchable PDFs with an invisible text layer. It is not for those who need a CDN-hosted script or a UMD build, nor for projects that cannot comply with AGPL-3.0. Before adopting, verify that your target browsers or Node versions support ESM and that you can serve all files from the same origin. Also review the AGPL-3.0 license implications for your distribution model. If you need a quick test, use the scribe.extractText function, but for production, plan to use openDocument and its methods.
Community notes