Skip to content
engine

engine

engine

The pure-Go (CGO_ENABLED=0) TeX engine. A faithful re-implementation of TeX that loads and runs the genuine LaTeX classes — \documentclass{article}, {report} and {book} execute the real, embedded .cls files (LPPL, verbatim) — not an emulation — in native builds and in the browser via js/wasm, with no TeXLive and no server.

What it does

  • Runs the standard base classes for real. article, report and book load and execute the genuine embedded LaTeX .cls files, typesetting a numbered title, a \tableofcontents shaped by the class’s own \l@… macros (the indent and number-box widths are read out of its \@dottedtocline calls, so article’s leaderless bold \l@section and book’s dotted one at the same level come out differently), numbered sections/subsections, \chapter (report/book, with its entry in the contents list and its opening page’s folio at the foot), numbered figure/table captions, itemize/enumerate, and math.
  • A full pipeline. A category-code mouth and gullet, a scaled-point stomach with Knuth–Plass line breaking and a cost-based page builder, \halign tables, math via math (vector output), OpenType fonts (a built-in default font, kerning, ligatures), and PDF + SVG output — the SVG carries a source map for click-to-line.
  • Floats and two columns are the default. Real float placement (top, bottom and float-page, with the seven parameters read from the class rather than from article’s) and the two-column layouts that classes set themselves (revtex reprint, acmart sigconf/sigplan, IEEEtran) are on without a flag; GOTEX_FLOATS=0 and GOTEX_TWOCOLUMN=0 opt back out. This page described both as still behind their environment variables, which they have not been for some time.
  • In the browser. A self-contained js/wasm build renders real LaTeX classes client-side — no TeXLive, no server.

Pipeline stages

Each stage is gated by an objective oracle.

StageStatusWhat it covers
Mouth + gullet✅ doneCategory-code tokenizer, eqtb, macros (delimited parameters, full expansion), conditionals, registers, grouping.
Stomach✅ doneBox/glue/penalty model in scaled points, Knuth–Plass line breaking, a cost-based page builder that keeps TeX’s two page measurements apart — page_total, which the break is decided on, and page_depth, which is not charged against the goal and is capped at \maxdepth — and \halign tables.
Math✅ done$…$ and display environments delegated to math (vector output).
Fonts✅ doneOpenType via go-opentype; a built-in default font, kerning and ligatures — runs with no assets.
Output✅ donePDF (embedded subset fonts, selectable text) and self-contained SVG pages with a click-to-line source map.
Real classes✅ done\documentclass{article|report|book} loads and runs the genuine embedded LaTeX class, native and js/wasm.

Fidelity gates

  • a byte-exact conformance ratchet (TestConformance), TeX snippets checked byte-for-byte against real-TeX output; and

  • a whole-document prose fidelity check against a real LaTeX engine (tectonic). On a corpus of 154 real arXiv papers with a reference PDF each, a vocabulary-bounded word census puts the median at 101% of the reference engine’s prose words (measured 2026-10-04); 10 of the 154 fall below 95% and 3 below 90%. Pagination lands on the reference exactly for 32 of them, with a total page deviation of 304 over the corpus (v0.261.0, font-controlled).

    In the default configuration — a different measurement, not an update of the one above, and the two must not be mixed — v0.263.0 lands exactly on 36 of the 154 with Σ|deviation| 294 and a signed bias of +72 pages. The bias is where the work is: it is a one-column phenomenon (#622), and v0.263.0 took 7 pages off it by keeping page_total and page_depth apart in the page builder. A page whose material reached exactly \textheight had been refused over the descender of its own last line, and lost a line (tex.web 19519-19529, 19594-19599).

    A median above 100% is expected rather than flattering: the engine emits each formula’s source as an invisible text layer so that maths can be found, copied and read aloud, and the census counts it. The figure to watch is the tail — the papers below 95% — not the median.

  • a geometry check, which is the only one of the three that can see text drawn outside the sheet. A block that should fill a title page used to be set as one horizontal list that was never line-broken, so everything past the right margin was placed off the paper — up to x=6619pt on a 612pt page, author names, affiliations and e-mails among it. The page count cannot see that (the clearest witness is 15 pages against 15), the ink count cannot (every glyph is emitted) and the text layer carries the words. Measured as lines ending beyond any page width this corpus uses: 49 papers at v0.260.0, 23 at v0.261.0, 22 at v0.262.0, against a floor of 2 on the reference PDFs themselves. The widest line went from 6619pt at v0.260.0 to 3357pt at v0.261.0 and 1788pt at v0.262.0.

Roadmap

The engine loads and runs the standard base classes; it is functional-parity-oriented and held to real-LaTeX fidelity, not a claim of full TeXLive parity.

  • Heavy packages (tikz, …) run as real files rather than native stubs; TikZ/pgf drawing is gated behind GOTEX_PGF and in bring-up.
  • Front matter that still reaches past the page edge: 22 of the 154 corpus papers, down from 49, with five causes closed and the remainder a different population each (engine#629). The fifth cause is \unvbox inside a paragraph: a vbox holds finished lines, and handing them to the line breaker glues them end to end and re-breaks the lot as one paragraph (acmart writes \noindent\unvbox\mktitle@bx).
  • A broader real-document conformance corpus (PDF-diff against pdftex/xetex) and the TRIP test.

Install

go get github.com/go-tex/engine

Requires Go 1.26.4 or newer. CGO_ENABLED=0, go vet clean, CI green across three 64-bit arches under QEMU (riscv64/ppc64le/s390x) plus macOS, Linux and Windows, and both js/wasm and wasip1/wasm.

Links

See the module’s README for full, up-to-date details.