Docs
mathra turns an equation (LaTeX, MathML or AsciiMath) into an SVG that carries its own speech and braille. The JavaScript, Python, Rust and terminal packages run the same core, so an equation gives the same SVG, byte for byte, on all four. New here? Watch the 60-second demo.
Install
| Where | Install | Registry |
|---|---|---|
| JavaScript and TypeScript: Node, bundlers, browsers | npm install @mathlabs/mathra | npm |
| Python 3.9+: wheels for Linux, macOS and Windows | pip install mathra | PyPI |
| Terminal | cargo install mathra-cli | crates.io |
| Rust | cargo add mathra | crates.io · docs.rs |
Types are included for TypeScript and Python. Nothing else is needed: the font (STIX Two Math) is embedded.
First equation
Node
import { texToSvg, render } from "@mathlabs/mathra"; // or require("@mathlabs/mathra")
const svg = texToSvg(String.raw`x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}`);
const { speech } = render(String.raw`\frac{3}{4}`, { locale: "fr" }); // "trois quarts"
Browsers and bundlers (Vite, webpack, esbuild)
Await init() once. It fetches the WebAssembly next to the module.
import { init, texToSvg } from "@mathlabs/mathra";
await init();
document.querySelector("#eq").innerHTML = texToSvg(String.raw`e^{i\pi} + 1 = 0`);
Without a bundler, from a CDN:
<script type="module">
import { init, texToSvg } from "https://cdn.jsdelivr.net/npm/@mathlabs/mathra/web/index.js";
await init();
document.body.innerHTML = texToSvg("3,14", { locale: "pt-BR" });
</script>
Python
import mathra
svg = mathra.tex_to_svg(r"\sin x = 0.5")
r = mathra.render(r"\frac{3}{4}", locale="fr") # a dict
r["speech"] # 'trois quarts'
Terminal
$ mathra 'f(1, 5) = \sin x - 2.5' -o function.svg $ mathra '3,14' --locale pt-BR --explain
Rust
let r = mathra::convert("3.14", &mathra::Options::default())?;
println!("{}", r.svg);
println!("{}", r.speech);
Options
The same five options everywhere, under each platform's spelling.
| Option | JavaScript | Python | Terminal | Rust (Options) |
|---|---|---|---|---|
| Language | locale: "pt-BR" | locale="pt-BR" | -l, --locale pt-BR | locale: Locale::PtBr |
| Inline style | display: false | display=False | --inline | display: false |
| Refuse broken LaTeX | strict: true | strict=True | --strict | strict: true |
| Input notation | input: "mathml" | input="mathml" | -f, --from mathml | input: Input::MathMl |
| Braille code | braille: "cmu" | braille="cmu" | --braille-code cmu | braille: Some(BrailleCode::Cmu) |
| The SVG writes itself | animate: true | animate=True | --animate | animate: true |
| Each element numbered by its step | steps: true | steps=True | --steps | steps: true |
- Language:
enby default. The 16 codes are under Languages. A region is accepted (es-MX,zh-CN). In JavaScript, a plain string as the second argument is read as the locale:texToSvg(tex, "fr"). - Inline style: display style (as in
$$…$$) is the default; inline is the style of$…$, with smaller limits and fractions. - Strict: off by default, so broken input is drawn and reported as warnings. On, it is refused like TeX does: an exception in JavaScript,
ValueErrorin Python, anErrin Rust, exit code 1 in the terminal. - Input:
latexby default,mathmlorasciimath. MathML passed as LaTeX (starting with<math) is recognized on its own. - Braille code: by default the language's: CMU in pt-BR and es, Nemeth elsewhere.
What comes back
texToSvg / tex_to_svg return the SVG as a string. render (and convert in Rust) return everything at once:
| Field | What it is |
|---|---|
svg | The standalone SVG (see Showing it in a page). |
speech | The phrase a screen reader says, in the language of the locale. The same phrase is in the SVG's <title>. |
text | Linear text close to UnicodeMath: (n ¦ k), x^2. It is the SVG's selectable text layer. |
braille, braille_code | The equation in Unicode braille cells, and the code used: "cmu" or "nemeth". |
tree | The tree of meaning: what each piece is ({"type": "number", "value": "3.14"}). Also in the SVG's <metadata>. |
applied | The numbered rules that changed something in this equation, as {rule, description}, in the order they acted. |
warnings | What tolerant mode fixed (E-REC-*), as {rule, description}. Always empty in strict mode. |
Also: locales() lists the languages as {code, name}, and version (JavaScript), core_version (Python) or mathra::VERSION (Rust) give the version of the core inside the package. Python also has tex_to_tree, the tree as a JSON string.
Showing it in a page
Inline, in the HTML
Put the SVG string in the page (innerHTML, a template, server-side rendering). This is the way to get everything:
- Color: glyphs are
fill="currentColor", so the equation takes the color of the text around it, and dark mode just works. - Size and baseline:
widthandheightare inem, so the equation follows the font size, andvertical-alignlines its baseline up with the text. - Selection: a transparent text layer holds the linear text, so selecting, copying and find-in-page work.
- Many on one page: ids are unique to each equation, so they do not clash.
As an image
<img src="equation.svg" alt="…"> works too, but an image cannot see the page: it draws in a fixed dark ink (#111), and nothing in it is selectable. Give the <img> the speech as its alt.
What else travels inside
role="img" and aria-labelledby point to the <title> with the speech. <metadata> keeps the source with its notation, the tree, the braille, the font name and any warnings, so the equation can be recovered from the file. The same input always gives the same bytes. The full contract is spec/svg.md.
Broken input
LaTeX copied from OCR, chat apps, HTML editors or unescaped JSON is often broken. By default mathra draws what it can and returns a warning for each fix. With strict, it refuses instead. A valid equation gives the same result in both modes.
$ mathra '\vertA\vert = C_{16,2}' --text
mathra: warning E-REC-1: column 1: unsupported command: \vertA; read as \vert A
|A| = C_(16, 2)
| Rule | Problem | What mathra does |
|---|---|---|
| E-REC-1 | Unknown command | Reads \vertA as \vert A, \nlesssim as \not\lesssim; otherwise writes the name in red |
| E-REC-2 | Leftover JSON escape (\n, ²) | A space, or the character |
| E-REC-3 | Double script (x_1_2) | The second script goes on an empty base |
| E-REC-4 | Unbalanced braces | A stray } is ignored; an open { is closed at the end |
| E-REC-5 | Unpaired \left, \right, \begin, \end | Closed with an empty delimiter, or ignored |
| E-REC-6 | Unknown environment | Read as a centered table |
| E-REC-7 | Missing argument (\frac{1}) | Left empty |
| E-REC-8 | Character the font lacks | A box (□) |
| E-REC-9 | Others: stray #, invalid color, control characters, \divchave with a zero divisor… | Drawn as written, or ignored |
| E-REC-10 | MathML: unknown element or entity, wrong number of children | Children read as a row; unknown entity dropped |
Warnings appear in warnings, on the terminal's stderr, in --explain and in the SVG's <metadata>. Broken XML in MathML, and inputs past the size limits (64 levels of nesting, 2,000 macro expansions), are errors in both modes. Details: spec/tolerance.md.
The command line
mathra <equation> [options] echo '<equation>' | mathra [options]
| Option | What it does |
|---|---|
-o, --output <file> | Write the SVG to a file instead of printing it |
-l, --locale <code> | The language (en by default) |
-f, --from <format> | latex (default), mathml or asciimath |
--inline | Inline style, as in $…$ |
--strict | Refuse invalid LaTeX, like TeX |
--explain | Show the text, the speech, the braille, the tree and the rules applied |
--tree, --speech, --text, --braille | Print only that output |
--braille-code <c> | cmu or nemeth |
--animate | The SVG writes itself in the browser, in the order the layout set it (CSS in the SVG) |
--steps | Each element numbered by that order (data-step), with no animation |
--batch | One equation per input line; with -o <folder> (it must exist), writes 1.svg, 2.svg…; prints ok, warning⇥… or error⇥… per line |
-V, --version, -h, --help | Version and help |
$ mathra --batch -o out/ < equations.txt ok warning E-REC-1 column 1: unsupported command: \vertA; read as \vert A
Languages
The locale decides how the equation is read, written and spoken. In pt-BR 3,14 is one number with a decimal comma, \sin is written sen and ]a, b[ is an open interval. In fr \frac{3}{4} is spoken "trois quarts".
en (default), pt-BR, es, fr, de, it, ru, zh, ja, ko, hi, ar, id, tr, nl, pl.
English and pt-BR are reviewed. The speech of the other languages still needs a native speaker's review; corrections are welcome as pull requests to the language's JSON file (spec/locales.md).
Braille
Every equation also comes out in braille, as Unicode braille cells: in the CMU (Código Matemático Unificado) for Portuguese and Spanish, and in Nemeth for the other languages, or in either by choice.
$ mathra '\frac{3}{4} + \sqrt{x}' --locale pt-BR --braille # ⠼⠒⠙⠖⠫⠱⠭
$ mathra 'x^2 + 1' --braille # ⠭⠘⠆⠐⠬⠂ (Nemeth)
Both codes follow their official documents and were compared with MathCAT, but they have not been reviewed by a braille transcriber yet. How far to trust them.
What it converts
Fractions, roots, scripts and limits, stretchy delimiters, matrices and array, cases, alignments, accents and braces, \text, alphabets, every amssymb symbol and negation, \cancel, \boxed, colors, siunitx, mhchem, physics, braket, tensor, mathtools, \newcommand and friends, commutative diagrams (amscd, tikz-cd, xymatrix), structural chemistry (chemfig structures, reaction schemes and mechanism arrows, read aloud by their molecular formulas), and the computed \divchave and \contaarmada. Every construction, drawn and in braille, is in the reference; the full list is in the README.
Out of scope, because mathra is for equations: TikZ and TeX programming (\ifx, \catcode). Not yet: automatic line breaking, MathML and PNG output, content MathML.
Specs and source
Every decision is a numbered rule with a test: rules, the SVG, input notations, tolerance, languages, braille, chemistry, the boundary. The ideas behind them are in the philosophy. The code is on GitHub, under MIT or Apache-2.0.