mathra

Philosophy

An equation is something to read, not a program to run.

TeX is a programming language that happens to typeset math. mathra is the opposite bet: a small library that only reads equations, understands what each piece means, and draws them with rules you can look up.

1

Equations, not TeX

The goal is to convert every equation, and nothing that is not one. To keep mathra from slowly growing into a rewritten TeX, every new construction goes through one rule:

The core takes what becomes boxes on a line or in a grid. Whatever needs coordinates, curves or running a program stays out.
InOut
Fractions, scripts, roots, delimiters, matrices, cases, alignmentsTikZ, pgfplots, \chemfig
amscd diagrams: straight arrows between neighboring cellstikz-cd, xymatrix: diagonal and curved arrows are geometry
\newcommand, \def with #1…#9, \DeclareMathOperator\ifx, \expandafter, \catcode: conditionals are running a program

Saying no is what keeps the library small, fast and predictable.

2

Meaning first, drawing second

Before anything is drawn, mathra builds a tree of meaning: this is the number 3.14, this is the identifier x, this is a fraction whose numerator is… Drawing, speech and plain text are three readings of the same tree.

$ echo '3,14' | mathra --locale pt-BR --tree
{"type":"row","children":[{"type":"number","value":"3.14"}]}

That is why the speech reads one number, not “3 comma 14”, why a locale can change how a number is written without changing what it is, and why the tree travels inside every SVG.

3

Every decision is a numbered rule

Spacing, decimal commas, how \sin is spelled, what to do with a stray brace: each one is a rule with a stable id in spec/rules.md. The code cites the id where the rule acts, the test starts with the same id, and --explain tells you which rules shaped your equation.

$ mathra '3,14' --locale pt-BR --explain
rules
  L-NUM-1   '3,14': comma between digits read as decimal separator (3.14)
  L-NUM-2   3.14 written with ',' as decimal separator (pt-BR)

When the output surprises you, there is a name to look up, not a heuristic to reverse-engineer.

4

The locale decides the form

Math is written differently in different places. In Brazil, 3,14 is one number, 1.000 is one thousand, the sine is sen and ]a, b[ is an open interval. These are not translations bolted on at the end: the locale changes how the input is read, and the speech comes out in the reader's language.

mathra speaks 16 languages, from English and Português to 中文, हिन्दी and العربية. Each one is a single JSON file of conventions and phrase templates, so adding a language, or fixing how one reads, needs no Rust.

5

Accessible by default, not by plugin

An equation that a screen reader announces as “image” is broken, however good it looks. Every mathra SVG carries its spoken form in <title>, linked with aria-labelledby, plus a transparent text layer so it can be selected and copied. There is nothing to turn on.

6

Forgive, but say so

Real LaTeX is copied from OCR, chat apps, HTML editors and JSON that was never unescaped. TeX refuses all of it. mathra draws what it can and records what it fixed, as numbered warnings (E-REC-1 to E-REC-9). Recovery never changes the meaning of what was already correct: a valid equation produces the same tree 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)

When you would rather fail, --strict refuses invalid input, like TeX.

7

Same input, same bytes

No timestamps, no random ids, no dependence on the fonts installed on the machine: the font is embedded, and ids are derived from the equation. Converting twice gives byte-identical files, so outputs can be cached, diffed and checked into a repository, and the README's own images are verified by the test suite.

8

The image is the whole thing

The SVG needs no stylesheet, no web font and no script to display. It takes its color from the page, its size from the surrounding text, and works in an email, a README, a slide, a PDF or an <img> tag. The source LaTeX and the tree ride along in <metadata>, so the image can always be traced back to what produced it.

9

One core, no runtime

The core is plain Rust, with three small dependencies: one to read the font and two for JSON. The npm, Python, CLI and Rust packages are thin wrappers around the same code, compiled to native code or to WebAssembly, so a server, a notebook and a browser produce the same equation, byte for byte. No TeX distribution and no headless browser; outside JavaScript, not even Node: 17.9 µs per equation natively.

Read the rules, try the playground, or tell us where it is wrong.