texlyre-busytex
v1.4.0
Published
Run LaTeX compilation (pdflatex, xelatex, lualatex) in the browser using WebAssembly (TeX Live 2026)
Maintainers
Readme
TeXlyre BusyTeX
Run LaTeX compilation directly in your browser using WebAssembly. Supports TeX Live 2026 XeLaTeX, pdfLaTeX, and LuaLaTeX with BibTeX, makeindex, and browser-side shell escape handler integration.
Live Demo | TeX Live-on-demand & Build
Features
- XeLaTeX: Compile with XeTeX engine + bibtex8 + dvipdfmx
- PdfLaTeX: Compile with PdfTeX engine + bibtex8
- LuaLaTeX: Compile with LuaHBTeX engine + bibtex8
- Multi-file Support: Handle complex projects with multiple .tex and .bib files
- SyncTeX: Generate SyncTeX files for editor synchronization
- Browser-based: All compilation runs entirely in the browser with no server required
- Web Worker Support: Non-blocking compilation using Web Workers
- Browser Shell Escape: Register JavaScript shell handlers for controlled in-browser command emulation
Installation
npm install texlyre-busytexDownload Assets
BusyTeX requires WASM files (~32 MB) + data (90-400 MB) that are hosted on GitHub Releases:
# Download to default location (./public/core)
npx texlyre-busytex download-assets
# Or specify custom location
npx texlyre-busytex download-assets ./static/wasm
npx texlyre-busytex download-assets ./public/assetsAssets will be downloaded to <destination>/busytex/ directory.
Usage
Basic Example
import { BusyTexRunner, XeLatex } from 'texlyre-busytex';
const runner = new BusyTexRunner({
busytexBasePath: '/core/busytex'
});
await runner.initialize();
const xelatex = new XeLatex(runner);
const result = await xelatex.compile({
input: `\\documentclass{article}
\\usepackage{amsmath}
\\begin{document}
\\section{Introduction}
Hello, LaTeX!
\\begin{equation}
E = mc^2
\\end{equation}
\\end{document}`
});
if (result.success && result.pdf) {
const blob = new Blob([result.pdf], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
window.open(url);
}With BibTeX, MakeIndex, and Multiple Runs
const result = await xelatex.compile({
input: `\\documentclass{article}
\\begin{document}
\\cite{sample2023}
\\bibliographystyle{plain}
\\bibliography{references}
\\end{document}`,
bibtex: true,
makeindex: true,
rerun: true,
additionalFiles: [
{
path: 'references.bib',
content: `@article{sample2023,
title={Sample Article},
author={Author, John},
year={2023}
}`
}
]
});Multi-file Projects
const result = await xelatex.compile({
input: `\\documentclass{article}
\\begin{document}
\\input{chapter1.tex}
\\input{chapter2.tex}
\\end{document}`,
additionalFiles: [
{
path: 'chapter1.tex',
content: '\\section{Chapter 1}\nContent...'
},
{
path: 'chapter2.tex',
content: '\\section{Chapter 2}\nContent...'
}
]
});Using PdfLaTeX or LuaLaTeX
import { PdfLatex, LuaLatex } from 'texlyre-busytex';
const pdflatex = new PdfLatex(runner);
const result = await pdflatex.compile({ input: '...' });
const lualatex = new LuaLatex(runner);
const result2 = await lualatex.compile({ input: '...' });With Web Worker
const runner = new BusyTexRunner({
busytexBasePath: '/core/busytex',
verbose: true
});
await runner.initialize(true); // true = use Web WorkerBrowser Shell Escape Handlers
BusyTeX can enable controlled shell escape in the browser by loading JavaScript handler scripts. This does not run arbitrary native commands. Instead, handler scripts register supported commands with BusyTeX and emulate those commands inside the browser or worker context.
const result = await pdflatex.compile({
input: `\\documentclass{article}
\\usepackage{xcolor}
\\begin{document}
\\input{highlighted-output.tex}
\\end{document}`,
shellEscape: true,
shellHandlerScripts: [
'/shell-handlers/highlight-handler.js'
],
additionalFiles: [
{
path: 'snippet-javascript.txt',
content: 'const message = "Hello from BusyTeX";\nconsole.log(message);'
}
]
});A shell handler script can register a browser-side command:
register_shell_handler('texlyre-highlight', (argv, cwd, FS, PATH, Module) => {
const [, language, inputPath, outputPath] = argv;
const source = FS.readFile(inputPath, { encoding: 'utf8' });
const contents = `{\\ttfamily\n${source}\n\\par}\n`;
return {
exit_code: 0,
files: [
{
path: outputPath,
contents
}
]
};
});The registered handler receives:
argv: command-line arguments passed by TeXcwd: current working directoryFS: Emscripten filesystem APIPATH: path helper object from the runtimeModule: active Emscripten module
The handler may return:
{
exit_code: number;
files?: {
path: string;
contents: string | Uint8Array;
}[];
}This is useful for examples such as browser-side syntax highlighting, small code generators, diagram preprocessors, or other deterministic transformations that can safely run as JavaScript in the browser.
SyncTeX Support
const result = await xelatex.compile({ input: '...' });
if (result.synctex) {
const blob = new Blob([result.synctex], { type: 'application/gzip' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'main.synctex.gz';
link.click();
}API Reference
BusyTexRunner
constructor(config?: BusyTexConfig)Config Options:
busytexBasePath: Path to BusyTeX assets (default:'/core/busytex')verbose: Enable verbose logging (default:false)engineMode: Engine bundle mode:'combined','pdftex','xetex', or'luahbtex'(default:'combined')preloadDataPackages: Data packages to preload during initializationcatalogDataPackages: Data packages available for later loading
Methods:
initialize(useWorker?: boolean): Promise<void>- Initialize the runnerisInitialized(): boolean- Check if initializedterminate(): void- Clean up resources
XeLatex, PdfLatex, LuaLatex
constructor(runner: BusyTexRunner, verbose?: boolean)
compile(options: CompileOptions): Promise<CompileResult>CompileOptions:
input: Main LaTeX document contentbibtex?: Enable BibTeX compilation (default:false)biber?: Enable Biber for BibLaTeX bibliographies, replacing the bibtex8 pass (default:false)makeindex?: Enable MakeIndex for index generation (default:false)rerun?: Enable multiple TeX passes to resolve references, TOC, and index entries (default:false)verbose?: Verbosity level -'silent','info', or'debug'(default:'silent')additionalFiles?: Array of{ path: string, content: string | Uint8Array }dataPackagesJs?: Additional TeX Live data package scripts to loadremoteEndpoint?: Optional TeX Live remote endpoint for on-demand package fetchingshellEscape?: Enable browser-side shell escape command handling (default:false)shellHandlerScripts?: Array of JavaScript handler script URLs loaded before compilation when shell escape is enabled
CompileResult:
success: Compilation succeededpdf?: PDF output as Uint8Arraysynctex?: SyncTeX output as Uint8Arraylog: Compilation logexitCode: Process exit codelogs: Detailed log entries
Development
Clone and Setup
git clone https://github.com/TeXlyre/texlyre-busytex.git
cd texlyre-busytex
npm install
npm run download-assets
npm run buildRun Example
# build (first-time use only)
npm run build:pages-example
# run example
npm run pages-exampleThen open http://localhost:3000
Upload Assets (Maintainers)
# Create archive and upload to GitHub Releases
npm run upload-assetsLimitations
- Browser shell escape does not run arbitrary native programs. It only supports commands explicitly registered by JavaScript shell handler scripts.
- Features requiring unavailable native external tools, such as SVG/EPS conversion, are not supported unless a compatible browser-side handler is provided. Bibliography processing with
biberruns as a separate wasm module, loaded on demand the first time a.bcfcontrol file is produced. - When TeX Live endpoint URL is set, pdfTeX and XeTeX can run all packages available in
texlive-recommendedandtexlive-extrausingtexlive-basiconly. However, LuaTeX requirestexlive-recommendedat least for a considerable number of packages to work. - The example page relies on Emscripten's built-in
EM_PRELOAD_CACHE(IndexedDB) to persist downloaded.datapackages across page refreshes, but does not implement any additional caching layer on top of it for caching packages and fonts downloaded from the remote endpoint. For a production-ready environment with full caching and project management, use TeXlyre instead.
License
TeXlyre-BusyTeX is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE for the complete license text.
This project incorporates TeXlyre-BusyTeX WASM (AGPL-3.0), itself derived from BusyTeX WASM (MIT).
Acknowledgments
Built with BusyTeX - A WebAssembly port of TeX Live.
