mp-tikz-wasm GitHub ↗
mp-tikz-wasm · drop-in tags

Diagram tags

Every figure below is a <script> or custom tag in the HTML source, typeset by MetaPost, pdfTeX, LuaTeX and dvisvgm. On this copy of the page the figures were saved in advance — mpost-wasm --prerender tags.html wrote them to figures/ — so they load as small static files, and the engines start only for the deliberately broken figure at the bottom. Typeset everything in this tab instead.

waiting for the figures…

TikZ, the tikzjax way

A <script type="text/tikz"> holding just a tikzpicture. It is wrapped in a standalone document automatically; libraries come from data-libraries.

A whole document

If the body contains \documentclass, it is compiled as is — any class, any package in the bundles, pgfplots included.

Custom elements

<tikz-diagram> and <metapost-diagram> work the same way and render when they enter the page (a MutationObserver catches later additions). Inside a custom element the content is HTML, so write &lt; for <. A data-gdlibraries attribute (or any use of graph drawing or \directlua) switches that diagram to LuaTeX.

\begin{tikzpicture}[node distance=1.4cm] \node[circle,draw,fill=blue!15] (a) {$a$}; \node[diamond,draw,fill=red!15,right=of a] (b) {$b$}; \draw[-latex,thick] (a) -- node[above] {$f$} (b); \end{tikzpicture}
\graph[layered layout, nodes={draw,circle,fill=green!15,font=\small}, sibling distance=7mm, level distance=7mm] { a -> {b -> {d, e}, c -> {f, g}}; e -> g; };
draw fullcircle scaled 60 withpen pencircle scaled 1.2 withcolor (0.1,0.3,0.8); for i = 0 upto 11: draw origin -- 30 dir (30i) withcolor (i/11)[blue,red]; endfor label.top(btex $\oint_\gamma f = 2\pi i\sum {\rm Res}$ etex, (0,32));

More TikZ libraries

Every PGF/TikZ library is bundled, and so is spath3, whose knots and calligraphy libraries pgf does not ship: here decorations along a path, and a braid whose crossings the knots library finds and redraws.

\begin{tikzpicture} \begin{knot}[clip width=5, flip crossing=2] \strand[red!70!black, ultra thick] (0,0) .. controls +(1,0) and +(-1,0) .. (2,1) .. controls +(1,0) and +(-1,0) .. (4,0); \strand[blue!70!black, ultra thick] (0,1) .. controls +(1,0) and +(-1,0) .. (2,0) .. controls +(1,0) and +(-1,0) .. (4,1); \end{knot} \end{tikzpicture}

MetaPost script tags

<script type="text/metapost">: a body without beginfig becomes one figure; data-tex chooses the label engine.

OpenType fonts, under LuaTeX

With data-bundles="+opentype" on the loader, a diagram can use fontspec under data-engine="lualatex", or luaotfload directly in plain data-engine="luatex". data-fonts="woff2" embeds a subset of each face as a web font and writes real <text> instead of outlines: try selecting the words in the first figure. The bundle is opt-in because LaTeX under LuaTeX initialises luaotfload on every run once it can find it.

Errors

A broken diagram shows its diagnostics instead of failing silently.

How to use it on your page

<script type="module" src="https://your-host/mp-tikz-wasm/dist/auto.js" data-figures="figures/"></script>
<script type="text/tikz" data-libraries="arrows.meta">
  \begin{tikzpicture} \draw[->] (0,0) -- (2,1); \end{tikzpicture}
</script>
<metapost-diagram>draw fullcircle scaled 50;</metapost-diagram>

Host the dist/ folder (the .wasm files, the JavaScript and bundles/) anywhere static; the script finds them relative to itself. Rendered SVGs are cached in IndexedDB by content hash, so a revisit shows the figures without running TeX. Add data-bundles="+opentype" for fontspec, data-worker="off" to run the engines on the page's own thread instead of a Web Worker, and data-log="debug" to watch the engines' output in the console (the menu above calls mpTikzWasm.setLogLevel(), which does the same at any time).

To spare first visits too, save the figures next to the page: npx mpost-wasm --prerender page.html writes figures/figure-HASH.svg for every diagram on the page (HASH is six characters of the hash of the element's source and attributes; add --opentype when the page uses fontspec), or open the page and call mpTikzWasm.saveFigures() — the button above — which writes the same files into a folder you pick (Chrome, Edge) or downloads them as a zip. With data-figures="figures/" on the loader, each element loads its file if it exists and the engines start only for the figures that are missing. The name is the content: a changed diagram gets a new file, and an old one is simply never requested.