blog post image
Andrew Lock avatar

Andrew Lock

~9 min read

Rendering Math in HTML: MathML, MathML Core, and AsciiMath

In this post I discuss some lesser known HTML elements: the MathML (and MathMLCore) specification, and show how these can be used to render math equations in HTML. Finally I discuss the AsciiMath language for an easier way of writing math in Markdown documents.

What is MathML?

I've written a couple of algorithm/data structure related posts recently, such as this one on PriorityQueue, and this one on using it to implement Dijkstra's shortest-path algorithm. In those posts I wanted to provide some big O complexity estimates for the behaviour, which meant potentially needing mathematical notation, such as O(lognlogd). The only trouble was, I wasn't sure how to display that!

Well, I didn't realise, but there's been various W3C recommendations and specifications for mathematical markup language over the years. The earliest, Mathematical Markup Language (MathML) 1, was recommended in 1998, and was even included in Mozilla 1.0!

Since then there have been two more versions of MathML, with the second edition of MathML 3.0 approved as an ISO standard in 2015. MathML is designed to be a general-purpose specification for mathematical notation across many different domains: browsers, office suites, computer algebra systems, EPUB readers, LaTeX-based generators, you name it!

To give you a taste, to render a function like this:

x5α×7

you would use the following HTML elements, which I've commented to explain their meanings:

<math>             <!-- The main element, everything is nested in here  -->
  <mfrac>          <!-- A fraction: first child is the numerator, second is the denominator  -->
    <msup>         <!-- A number raised to a power: first child is the base, second child is the superscript -->
      <mi>x</mi>   <!-- <mi> defines an identifier, such as function name, variable or symbolic constant -->
      <msqrt>      <!-- Square root -->
        <mn>5</mn> <!-- A constant number literal -->
      </msqrt>
    </msup>
    <mrow>         <!-- A grouping element -->
      <mi>α</mi>   <!-- Another identifier -->
      <mo>×</mo>   <!-- An operator. Can also include things like parentheses -->
      <mn>7</mn>   <!-- Another number literal -->
    </mrow>
  </mfrac>  
</math>

Unfortunately, the broad applicability, large complexity, and relaxed requirements mean that MathML support in browsers was inconsistent, with large sections of the specification completely unimplemented.

Instead, a new draft specification has been developed, called MathML Core.

What is MathML Core?

According to the excellent MathML Core Explainer accompanying the draft specification:

MathML Core is an attempt to create a minimal version of MathML that is well aligned with the modern web platform. It aims to resolve long-standing issues with the split evolution of philosophies between MathML specifications and the larger web platform. As part of this aim, MathML Core creates a well-defined starting point based on what is currently widely implemented. By creating a minimal version of MathML, MathML Core has an increased focus on testability and interoperability.

One of the easiest ways to see that "minimal" focus is in the number of elements supported by both standards: MathML 3 contained 195 elements; MathML Core focuses on about 30, and some of those are deprecated, existing only for backwards compatibility!

For completeness sake (and for my own reference later) I've listed the non-deprecated elements from MathML Core below, along with an example of it rendered, and a link to the MDN docs for the element.

First of we have the root and semantic elements:

ElementPurpose
<math>Root element for MathML. Link
<semantics>Used to provide alternative representations of the content. For example, a png rendering of the image. Link
<annotation>Used in <semantics> to provide an alternative representation. Link
<annotation-xml>Used in <semantics> to provide an OpenMath or similar representation of the mathematics. Link

Next we have the main "token" elements, which make up most of the visible content of the rendered math. Many of these elements end up rendered the same in the browser, but remember that HTML is also about conveying a degree of semantic understanding as well as simply content:

ElementRenderPurpose
<mtext>Some commentDisplay text without any additional mathematical meaning, such as comments or annotations. Link
<mi>xAn identifier. Link
<mn>123A literal number. Link
<mo>(×)A general operator, including parentheses. Link
<mspace>A blank space with defined attributes. Link
<ms>Hello World!A string literal, as produced by a program, for example. Link

Next up we have some "grouping" elements:

ElementRenderPurpose
<mfrac>x2Used to display fractions. Link
<mrow>2x+1Doesn't render directly, but is used to group multiple elements, such as the denominator of the example fraction. Link
<msqrt>2Square roots. Link
<mroot>x3Other (non-square) roots. Link

There are 5 elements for working with superscripts and subscripts:

ElementRenderPurpose
<msub>x1Attach a subscript to an expression. Link
<msup>x2Attach a superscript to an expression. Link
<msubsup>x12Attach both a subscript and a superscript to an expression. Link
<mmultiscripts>XabcabcA generalized version of <msubsup> for attaching multiple prescripts and postscripts. Link
<mprescripts />Used with <mmultiscripts> to separate the prescripts from the postscripts. Link

There are 3 elements for drawing accents and limits directly under or over expressions:

ElementRenderPurpose
<munder>x+2Adds an accent or limit under an expression. Link
<mover>x+2Adds an accent or limit over an expression. Link
<munderover>x+2Adds an accent or limit both over and under an expression. Link

And finally we have 3 elements for drawing tables and matrices:

ElementRenderPurpose
<mtable>[abcd]Allows creating tables and matrices. Similar to <table>. Link
<mtr>A row in an <mtable>, equivalent to <tr> in <table>. Link
<mtd>A cell in an <mtr>, equivalent to <td> in <table>. Link

Reading the design goals of MathML Core is quite interesting, as they're trying to create a "workable" version of MathML that integrates better with expectations (such as being able to style with CSS), while also acknowledging that MathML is already well (if unevenly) implemented in many places.

As far as I can tell from caniuse.com MathML is generally well supported, with approximately 1 year of Chromium support for MathML Core, while Safari and Firefox (presumably) support MathML 3.0. There's no IE support (unsurprisingly) but overall availability is over 90%.

MathML

The only trouble is… it sure is ugly to write MathML elements 😬

Easier ways to write math

The lack of universal support for MathML in browsers and the general verbosity of MathML has lead to various alternative ways to get math in the browser. One of the most popular is MathJax which includes a whole JavaScript display engine. If you're willing to drop the MathJax script on your page, it will interrogate your HTML, and replace it with the MathML equivalent:

mathjax in action

In terms of getting started, MathJax is impressively simple. It lets you write equations using TeX/LaTeX definitions, which are much more succinct and easier to read than MathML. The output isn't actually MathML, rather it's custom MathJax elements. MathML is actually one of the inputs MathJax works with.

MathJax also supports the AsciiMath markup language. This is an even simpler way of describing mathematical equations (in my opinion) than LaTeX. It aims to do for writing math what "markdown" does for HTML.

AsciiMath: making it easier to write MathML

AsciiMath was originally created as way to more easily write MathML. An early implementation, ASCIIMathML.js, used a similar approach to MathJax: drop the file on your page, and it will scan for any AsciiMath notation and replace it for you.

The original home page for ASCIIMathML.js is still available with that "classic" university early 2000s look 😅.

AsciiMath was what I discovered while looking for a way to more easily include math in my blog markdown pages. While I will use LaTeX if forced (writing my PhD thesis with anything else would have been horrible to be fair), I still find it pretty cumbersome to work with. AsciiMath speaks to me 😅

I think the best way to understand it is to see it in action, so the following table shows a whole bunch of equations written in AsciiMath along with how they render to MathML

AsciiMathRendered MathML
x^2x2
a_mam
(x+1)/yx+1y
O ((log n) / (log d))O(lognlogd)
sqrt xx
[[a,b],[c,d]][abcd]
((a,b),(c,d))^(-1)(abcd)1
(-b-sqrt(b^2-4a*c))/(2a)bb24ac2a
int_(-1)^1 sqrt(1-x^2)dx = pi/2111x2dx=π2
lim_(N->oo) sum_(i=0)^NlimNi=0N
int_0^1 f(x)dx01f(x)dx

When you get to some of the more complex equations, things definitely start to get a bit harder to follow, but in general I was sold!

The only trouble is that I'm not a fan of the approach MathJax or ASCIIMathML.js take of "load this JavaScript file and it searches everywhere and rewrites the equations". That's just not how we do things these days.

For my blog I have a janky, custom, .NET app that loads Markdown files and prerenders things like the syntax highlighting rather than doing it in the browser, and I really wanted the same thing for AsciiMath rendering. The only problem…I couldn't find an AsciiMath parser for .NET, so I had to improvise. In the next post I'll show how I worked around it!

Summary

In this short post I gave a brief introduction to rendering mathematical equations in HTML using MathML. I then described the more recent draft specification MathML Core which aims to provide a subset of the MathML functionality, geared specifically towards the browser. I briefly discussed how MathJax works, by parsing the whole document to look for TeX or AsciiMath expressions in your document. This led finally to showing some example of AsciiMath in action. In the next post I'll show how I created a working AsciiMath parser for .NET

Andrew Lock | .Net Escapades
Want an email when
there's new posts?