KaTeX-compatible math rendering engine in pure Rust — no JavaScript, no WebView, no DOM.
Parse LaTeX, lay it out with TeX rules, and render it natively on any platform.
\frac{-b \pm \sqrt{b^2 - 4ac}}{2a} → iOS · Android · Flutter · React Native · Web · PNG
Every major cross-platform math renderer today runs LaTeX through a browser or a JavaScript engine. That means:
- A hidden WebView eating 50–150 MB of RAM
- JavaScript startup latency before the first formula appears
- No offline guarantee, no predictable performance
RaTeX cuts the web stack out entirely. One Rust core, one display list, every platform renders natively.
| KaTeX (web) | MathJax | RaTeX | |
|---|---|---|---|
| Runtime | V8 + DOM | V8 + DOM | Pure Rust |
| Mobile | WebView | WebView | Native |
| Offline | Depends | Depends | Yes |
| Bundle overhead | ~280 kB JS | ~500 kB JS | 0 kB JS |
| Memory model | GC / heap | GC / heap | Predictable |
| Syntax coverage | 100% | ~100% | ~99% |
- ~99% of KaTeX formula syntax — parses and lays out the same LaTeX source
- ~80% visual similarity to KaTeX (golden test score against KaTeX reference renders)
- One display list output: flat, serializable drawing commands consumed by any renderer
- C ABI (
ratex-ffi) for FFI from Swift, Kotlin, Dart, Go, C++ - WASM (
ratex-wasm) for drop-in browser use via<ratex-formula>Web Component - Server-side PNG via tiny-skia — no browser needed
→ Live support table: RaTeX vs KaTeX across all 916 test formulas
| Platform | How | Status |
|---|---|---|
| Web | WASM → Canvas 2D · <ratex-formula> Web Component |
Working |
| Server / CI | tiny-skia → PNG rasterizer | Working |
| iOS | Swift/ObjC bindings to C ABI | Binding layer in progress |
| Android | JNI → Kotlin/Java | Binding layer in progress |
| React Native | Native module via C ABI | Binding layer in progress |
| Flutter | Dart FFI via C ABI | Binding layer in progress |
The Rust core is complete. What remains is the thin per-platform binding layer.
Rendering a LaTeX formula goes through four stages: tokenization → parsing → layout → display list. The display list is a flat list of drawing commands (glyphs, lines, rectangles, paths) with absolute coordinates; it is consumed either by native UI (iOS/Android/Flutter/RN) or by the server-side rasterizer (tiny-skia → PNG).
flowchart LR
subgraph input[" "]
A[LaTeX string]
end
subgraph core["Rust core"]
B[ratex-lexer<br/>Tokenization]
C[ratex-parser<br/>AST]
D[ratex-layout<br/>LayoutBox tree]
E[to_display_list<br/>DisplayList]
end
subgraph output[" "]
F[Native render<br/>iOS / Android / Flutter / RN]
G[ratex-render<br/>PNG]
end
A --> B --> C --> D --> E
E --> F
E --> G
flowchart TB
subgraph types["ratex-types"]
T1[Color, PathCommand<br/>DisplayItem, DisplayList<br/>MathStyle]
end
subgraph font["ratex-font"]
F1[KaTeX font metrics<br/>symbol tables]
end
LEX[ratex-lexer<br/>Token stream] --> PARSE[ratex-parser<br/>ParseNode AST]
F1 -.-> LEX
F1 -.-> PARSE
PARSE --> LAYOUT[ratex-layout<br/>layout → LayoutBox]
F1 -.-> LAYOUT
LAYOUT --> TODISP[to_display_list<br/>LayoutBox → DisplayList]
TODISP --> DL[DisplayList]
T1 -.-> DL
DL --> FFI[ratex-ffi<br/>C ABI]
DL --> RENDER[ratex-render<br/>tiny-skia → PNG]
DL --> WASM[ratex-wasm<br/>JSON for web]
- ratex-lexer: Turns the LaTeX source string into a stream of tokens (commands, braces, symbols, etc.).
- ratex-parser: Builds a ParseNode AST (KaTeX-compatible), with macro expansion and function dispatch.
- ratex-layout: Takes the AST and produces a LayoutBox tree (horizontal/vertical boxes, glyphs, rules, fractions, etc.) using TeX-style metrics and rules. Then to_display_list converts the LayoutBox tree into a flat DisplayList.
- DisplayList: Serializable list of
DisplayItems (GlyphPath, Line, Rect, Path). Consumed by:- ratex-ffi: Exposes the pipeline via C ABI for iOS/Android/RN/Flutter to render natively.
- ratex-render: Rasterizes the display list to PNG using tiny-skia (server-side).
- ratex-wasm: Exposes the same pipeline to the browser; returns DisplayList as JSON for Canvas 2D (or other) rendering.
| Crate | Role |
|---|---|
ratex-types |
Shared types: Color, PathCommand, DisplayItem, DisplayList, MathStyle. |
ratex-font |
Font metrics and symbol tables (KaTeX-compatible fonts). |
ratex-lexer |
LaTeX lexer → token stream. |
ratex-parser |
LaTeX parser → ParseNode AST (KaTeX-compatible syntax). |
ratex-layout |
Math layout engine: AST → LayoutBox tree → to_display_list → DisplayList. |
ratex-render |
Server-side only: rasterize DisplayList to PNG (tiny-skia + ab_glyph). |
ratex-ffi |
C ABI: full pipeline → DisplayList for iOS, Android, RN, Flutter to render natively. |
ratex-wasm |
WebAssembly: parse + layout → DisplayList as JSON for browser rendering. |
LaTeX formula string
↓
ratex-lexer → tokenization
↓
ratex-parser → ParseNode AST
↓
ratex-layout → LayoutBox tree → to_display_list → DisplayList
↓
ratex-ffi → display list (iOS / Android / RN / Flutter → native render)
or
ratex-render → server-side rasterize to PNG (tiny-skia)
or
ratex-wasm → DisplayList JSON (web)
Requirements: Rust 1.70+ (rustup)
git clone https://github.com/erweixin/RaTeX.git
cd RaTeX
cargo build --releaseecho '\frac{1}{2} + \sqrt{x}' | cargo run --release -p ratex-render
# With custom font and output directories
echo '\sum_{i=1}^n i = \frac{n(n+1)}{2}' | cargo run --release -p ratex-render -- \
--font-dir /path/to/katex/fonts \
--output-dir ./out<!-- 1. Fonts -->
<link rel="stylesheet" href="node_modules/ratex-web/fonts.css" />
<!-- 2. Register the Web Component -->
<script type="module" src="node_modules/ratex-web/dist/ratex-formula.js"></script>
<!-- 3. Done -->
<ratex-formula latex="\frac{-b \pm \sqrt{b^2-4ac}}{2a}" font-size="48"></ratex-formula>See platforms/web/README.md for the full WASM + web-render setup.
cargo test --all| Crate | Role |
|---|---|
ratex-types |
Shared types: DisplayItem, DisplayList, Color, MathStyle |
ratex-font |
KaTeX-compatible font metrics and symbol tables |
ratex-lexer |
LaTeX → token stream |
ratex-parser |
Token stream → ParseNode AST (KaTeX-compatible) |
ratex-layout |
AST → LayoutBox tree → DisplayList |
ratex-ffi |
C ABI: exposes the full pipeline for native platforms |
ratex-wasm |
WASM: pipeline → DisplayList JSON for the browser |
ratex-render |
Server-side: DisplayList → PNG (tiny-skia) |
- Formula support (~99%): The same LaTeX source rendered by KaTeX in the browser and by RaTeX on device. We continue to close remaining gaps.
- Visual similarity (~80%): Golden tests compare RaTeX-rendered PNGs to KaTeX reference PNGs using an ink-coverage score (IoU of ink pixels, recall, aspect and width similarity). 80% is the visual likeness score — not the share of formulas supported, which is ~99%.
Ratex owes a great debt to KaTeX. KaTeX is the de facto reference for fast, rigorous LaTeX math on the web; its parser, symbol tables, and layout semantics follow Donald Knuth's TeX standard. We use KaTeX’s font metrics and golden outputs to validate Ratex, and we aim for syntax and visual compatibility so that the same LaTeX source can be rendered consistently by KaTeX in the browser and by Ratex on native platforms. We thank the KaTeX project and contributors for their open, well-documented work—without it, this engine would not exist.
MIT — Copyright (c) erweixin.