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,reportandbookload and execute the genuine embedded LaTeX.clsfiles, typesetting a numbered title, a\tableofcontentsshaped by the class’s own\l@…macros (the indent and number-box widths are read out of its\@dottedtoclinecalls, soarticle’s leaderless bold\l@sectionandbook’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,
\haligntables, math viamath(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 (revtexreprint, acmartsigconf/sigplan, IEEEtran) are on without a flag;GOTEX_FLOATS=0andGOTEX_TWOCOLUMN=0opt 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/wasmbuild renders real LaTeX classes client-side — no TeXLive, no server.
Pipeline stages
Each stage is gated by an objective oracle.
| Stage | Status | What it covers |
|---|---|---|
| Mouth + gullet | ✅ done | Category-code tokenizer, eqtb, macros (delimited parameters, full expansion), conditionals, registers, grouping. |
| Stomach | ✅ done | Box/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 | ✅ done | OpenType via go-opentype; a built-in default font, kerning and ligatures — runs with no assets. |
| Output | ✅ done | PDF (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; anda 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
defaultconfiguration — 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 keepingpage_totalandpage_depthapart in the page builder. A page whose material reached exactly\textheighthad been refused over the descender of its own last line, and lost a line (tex.web19519-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 behindGOTEX_PGFand 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
\unvboxinside 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/engineRequires 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
- Source: https://github.com/go-tex/engine
- API reference: https://pkg.go.dev/github.com/go-tex/engine