Mathématiques sur le Web (MathJax / KaTeX)

TeX ne tourne pas dans un navigateur. Les outils qui mettent des mathématiques LaTeX sur une page webMathJax et KaTeX — sont donc deux réimplémentations de la composition mathématique de TeX en JavaScript, et tous deux ont parié à l'inverse l'un de l'autre sur le même problème. Composer ici les mêmes 500 formules a demandé 45 millisecondes à KaTeX et 264 à MathJax. Choisir sur la seule vitesse, pourtant, expose à trébucher ailleurs : ni l'un ni l'autre ne traite $...$ comme des mathématiques par défaut. Cette page traite de la différence de conception, des messages d'erreur que l'on voit réellement, et de la survie du TeX que l'on colle dans une page.

KaTeX ou MathJax : lequel mettre sur la page

Beaucoup de formules et la vitesse compte : KaTeX. Le LaTeX écrit doit passer tel quel : MathJax. L'écart de vitesse ne tient pas à qui a optimisé le plus fort, mais à la forme de l'API. Le katex.renderToString(...) de KaTeX renvoie une chaîne de façon synchrone : le HTML est là au moment de l'appel, rien n'est injecté après coup, rien ne saute. MathJax, dans le navigateur, est bâti autour de MathJax.typesetPromise(), qui fait ce que son nom annonce et renvoie une promesse. Cette brève apparition d'un \frac{1}{2} brut qu'aperçoit parfois le lecteur est un effet de bord de cette asynchronie.

html
<!-- MathJax 3: configure BEFORE the script tag loads -->
<script>
  window.MathJax = {
    tex: { inlineMath: [["$", "$"], ["\\(", "\\)"]] }
  };
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>

<!-- KaTeX: stylesheet, engine, then the auto-render pass -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex/dist/contrib/auto-render.min.js"
        onload="renderMathInElement(document.body);"></script>

Pourquoi $...$ ne s'affiche pas : le commentaire dans le source de KaTeX

Aucune des deux bibliothèques ne traite $...$ comme des mathématiques en ligne par défaut. Ce n'est pas un oubli mais un choix délibéré. Ouvrez le source de l'auto-render de KaTeX : l'entrée pour $ s'y trouve, commentée, avec la raison une ligne au-dessus — « LaTeX uses $…$, but it ruins the display of normal $ in text ». Deux symboles monétaires dans un paragraphe et tout ce qui les sépare devient une formule. Les valeurs par défaut de MathJax disent la même chose : inlineMath n'est que \(...\), tandis que displayMath vaut $$...$$ et \[...\]. Une bonne part des signalements « mes formules s'affichent en clair » se ramène exactement à cela.

js
// KaTeX auto-render, default delimiters (dist/contrib/auto-render.js):
//   { left: "$$",  right: "$$",  display: true  }
//   { left: "\\(",  right: "\\)",  display: false }
//   { left: "\\[",  right: "\\]",  display: true  }
//   plus \begin{equation} \begin{align} \begin{alignat} \begin{gather} \begin{CD}
//
// and this line is deliberately commented out in the source:
//   // LaTeX uses $...$, but it ruins the display of normal `$` in text:
//   // {left: "$", right: "$", display: false},

// turn it on yourself only if the page has no currency amounts:
renderMathInElement(document.body, {
  delimiters: [
    { left: "$$", right: "$$", display: true },
    { left: "$",  right: "$",  display: false },
    { left: "\\(", right: "\\)", display: false },
    { left: "\\[", right: "\\]", display: true }
  ],
  ignoredTags: ["script", "noscript", "style", "textarea", "pre", "code"]
});

Ce que KaTeX ne sait pas faire, et les messages qu’il affiche

KaTeX prend en charge un sous-ensemble des mathématiques LaTeX ; tout ce qui en sort est levé comme exception. La formulation est constante : KaTeX parse error: Undefined control sequence: \eqref at position 1: et ainsi de suite. Le manque le plus douloureux est le renvoi aux numéros d'équation : ni \label ni \eqref ne sont définis, si bien qu'un document qui numérote ses équations et y renvoie ne tient pas sur KaTeX seul. La chimie ne passe pas davantage : \ce{H2O} réclame l'extension distincte mhchem. L'autre écueil constant est KaTeX parse error: {align} can be used only in display mode., qui surgit lorsqu'un environnement align est écrit entre des délimiteurs en ligne.

C'est cependant une idée reçue que KaTeX ne gérerait pas les macros. \newcommand, \def et \gdef fonctionnent tous, et si l'on passe un objet vide à l'option macros, une définition faite par \gdef persiste d'un appel à l'autre. C'est ainsi que l'on injecte une fois pour toutes un préambule de macros valable sur tout le site. MathJax, de son côté, embarque une trentaine de réimplémentations de packages TeX — ams, amscd, mathtools, mhchem, cancel, braket, bussproofs, empheq, colortbl entre autres — et cette liste est ce que « compatibilité plus large » veut dire concrètement. Enfin, pour éviter qu'une formule fautive ne casse la page, retenez throwOnError: false : au lieu de lever une exception, KaTeX affiche le source fautif en rouge (#cc0000) et poursuit.

Ce que vous avez écritKaTeXMathJax
\frac \int \underbrace \textpassepasse
\newcommand \def \gdeffonctionne (persiste via macros)fonctionne (via la configuration macros)
\label \eqrefnonUndefined control sequencefonctionne (numérotation via l’option tags)
\ce{H2O}exige l’extension mhchem séparéeinclus
align (inline){align} can be used only in display mode.passe

MathML est-il utilisable ? Le trou comblé en 2023

Tous les grands navigateurs savent afficher MathML. Le siège resté vide le plus longtemps fut celui de Chrome : la prise en charge est entrée, ressortie, puis revenue pour de bon en version 109 (Edge à partir de la même 109). Firefox l'a depuis la version 2, Safari depuis la 10. Cela dit, il y a encore peu d'occasions d'écrire du MathML à la main. Ce qui compte davantage, c'est que les deux bibliothèques placent du MathML dans leur sortie. KaTeX pose toujours un élément <math> à côté du HTML visible, et le paquet standard tex-mml-chtml.js de MathJax charge d'emblée l'extension qui ajoute du MathML pour les technologies d'assistance. C'est à cette couche cachée qu'un lecteur d'écran doit de pouvoir lire la formule.

Le TeX survit-il ? Duquel peut-on recopier les formules

Avec KaTeX, il survit ; avec MathJax, par défaut, non. Tout arbre MathML produit par KaTeX contient une <annotation encoding="application/x-tex">, et le TeX d'origine y est logé. Donnez-lui x^2+1 et la chaîne x^2+1 demeure quelque part dans la sortie. La distribution livre en outre une extension copy-tex dont le commentaire de source dit : « Replace .katex elements with their TeX source » — chargée, elle fait que copier une formule sélectionnée met $x^2+1$ dans le presse-papiers plutôt qu'une rangée de glyphes. La sortie HTML ordinaire de MathJax ne transporte pas le TeX d'origine. Sa réponse est le menu contextuel : « Show Math As », d'où la forme initiale se récupère.

html
<!-- what KaTeX puts in the DOM for x^2+1 -->
<span class="katex"><span class="katex-mathml"><math
    xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow>
  <msup><mi>x</mi><mn>2</mn></msup><mo>+</mo><mn>1</mn>
  </mrow><annotation encoding="application/x-tex">x^2+1</annotation>
</semantics></math></span><span class="katex-html" aria-hidden="true">...</span></span>

<!-- load this and Ctrl-C on a formula yields $x^2+1$, not glyphs -->
<script defer src="https://cdn.jsdelivr.net/npm/katex/dist/contrib/copy-tex.min.js"></script>

Faire parvenir vos macros jusqu’au navigateur

C'est ici que passe la frontière avec les outils qui convertissent un document entier en HTML. MathJax et KaTeX ne reçoivent que des fragments de mathématiques ; ni l'un ni l'autre ne lit votre préambule. Un \newcommand{\R}{\mathbb{R}} en tête du .tex n'atteint donc jamais le navigateur, et \R s'affiche indéfini, en rouge. Les macros doivent être transmises à part, par l'objet de configuration. Le même piège guette du côté des convertisseurs : le mode mathjax de make4ht laisse les mathématiques en LaTeX dans le HTML, si bien que vos macros n'y sont pas davantage développées. Le versant convertisseur relève de « LaTeX → HTML », mais le remède est identique des deux côtés — rassembler les définitions de macros dans un seul fichier et le donner à lire aussi bien à TeX qu'à JavaScript.

js
// KaTeX: one shared object, and \gdef survives from call to call
const macros = {};
katex.renderToString("\\gdef\\R{\\mathbb{R}}", { macros });
katex.renderToString("f\\colon \\R \\to \\R", { macros });   // \R resolves

// or declare them up front, the same way for the auto-render pass:
renderMathInElement(document.body, {
  macros: { "\\R": "\\mathbb{R}", "\\eps": "\\varepsilon" },
  throwOnError: false          // print the bad source in red, do not break the page
});

// MathJax 3: the equivalent lives in the config object
window.MathJax = {
  tex: {
    macros: { R: "\\mathbb{R}", eps: "\\varepsilon" },
    tags: "ams"                // this is what enables \label and \eqref
  }
};
  • Les formules s’affichent en clair → soupçonner les délimiteurs. $...$ est désactivé par défaut chez KaTeX comme chez MathJax.
  • Le document emploie \eqref → choisir MathJax et poser tags: "ams". KaTeX n’a aucun mécanisme de numérotation.
  • Une page-liste avec des centaines de formules → KaTeX. Le rendu synchrone évite que la mise en page saute après coup.
  • Vous avez des macros maison → les passer dans macros. Votre préambule n’atteint jamais le navigateur.
  • Une formule fautive ne doit pas emporter la pagethrowOnError: false dans KaTeX : affichage en rouge, et on continue.
  • Vous voulez tout le document sur le web → ce n’est pas le travail d’un moteur de rendu mathématique (voir « LaTeX → HTML »).