Skip to content

TeXlyre/texlyre-busytex

Repository files navigation

TeXlyre BusyTeX

Run LaTeX compilation directly in your browser using WebAssembly. Supports TeX Live 2026 XeLaTeX, pdfLaTeX, and LuaLaTeX with BibTeX and makeindex integration.

npm version License: AGPL v3

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

Installation

npm install texlyre-busytex

Download 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/assets

Assets 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 Worker

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)

Methods:

  • initialize(useWorker?: boolean): Promise<void> - Initialize the runner
  • isInitialized(): boolean - Check if initialized
  • terminate(): void - Clean up resources

XeLatex, PdfLatex, LuaLatex

constructor(runner: BusyTexRunner, verbose?: boolean)
compile(options: CompileOptions): Promise<CompileResult>

CompileOptions:

  • input: Main LaTeX document content
  • bibtex?: Enable BibTeX compilation (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 }

CompileResult:

  • success: Compilation succeeded
  • pdf?: PDF output as Uint8Array
  • synctex?: SyncTeX output as Uint8Array
  • log: Compilation log
  • exitCode: Process exit code
  • logs: 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 build

Run Example

# build (first-time use only)
npm run build:pages-example
# run example
npm run pages-example

Then open http://localhost:3000

Upload Assets (Maintainers)

# Create archive and upload to GitHub Releases
npm run upload-assets

Limitations

  • Fonts must be referenced by filename rather than by font name, e.g. \setmainfont{FiraSans-Regular.otf} instead of \setmainfont{Fira Sans}.
  • Features requiring external tools such as SVG/EPS inclusion, bibliography processing with biber, or shell escape (e.g. minted) are not supported in WebAssembly.
  • When TeX Live endpoint URL is set, pdfTeX and XeTeX can run all packages available in texlive-recommended and texlive-extra using texlive-basic only. However, LuaTeX requires texlive-recommended at 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 .data packages 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 richer 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.