Interoperability makes the web better for everyone, allowing users to have a great experience regardless of their choice of browser. We have been working on MathML Core making across browser engines as part of an agreement with the Sovereign Tech Fund. There are some exciting developments and new features!
Interoperability makes the web better for everyone, allowing users to have a great experience regardless of their choice of browser. We have many standards that shape how the internet should work, drafted from consensus between different engine makers and third parties. While having specs on how everything should function is great, we still need to align the different browser implementations. This can be tricky as all of them have their peculiarities, and not all browsers agree on what is a priority for them. The goal of the Interop program is to select a few important features that all engines will prioritize, so users and editors can finally benefit from them.
A few months ago I joined Igalia's web platform team (and I'm really happy about it!). Thanks to an agreement with the Sovereign Tech Fund, this year we will be working on MathML and other important Interop areas.
This post contains MathML examples. Each formula is represented twice. Your browser renders the left one from the HTML code, while on the right there is a pre-printed SVG as a reference of how it should look. Keep in mind that most of these features are either experimental or have just landed, so you may need the latest version of a browser to view them correctly.
A bit of history
MathML was first published in 1998, and it grew to be a gigantic project that sought to define how mathematical notation should be rendered. However, due to its complexity, the implementations of the browser engines were wildly different and incomplete. This meant that editors could not rely on it, since users would see very different content depending on what they were browsing with.
<math>
<msubsup>
<mo>∫</mo>
<mn>0</mn>
<mn>1</mn>
</msubsup>
<mrow>
<msup>
<mi>x</mi>
<mn>2</mn>
</msup>
<mo>+</mo>
<mn>1</mn>
</mrow>
</math>
This is why MathML Core was born. It is a small subset of MathML 3 that is feasible to implement in browsers. It is based on the parts of the specification that are used in practice, adding important implementation details and testing.
To illustrate why this is important, Chromium had support for some parts of MathML when it was forked from WebKit. However, it proved to be very difficult to maintain and complete, so it was removed in 2013. My colleague Frédéric Wang led the effort to create a new implementation based on MathML Core, which was shipped in 2023, a huge milestone for the standard.
We are in a very exciting moment in the MathML history, since all three major browser engines have overlapping support. However, there is still work to be done to align the different implementations so they follow the MathML Core specification. The goal is that one could write formulas on a website and have it look the same everywhere (like Wikipedia, which is now transitioning to native MathML instead of prerendered SVGs).
So, what have we been working on?
RTL mirroring
Some scripts are written from right to left, including Arabic.
Browsers should be able to correctly render text and math in either direction, making use of the Unicode BiDi specification and the rtlm font feature.
However, the existing implementations either didn't support mirroring or had hacky behaviour that didn't work correctly for all cases. Read this explainer that Frédéric made for a great visualization of the differences.
<link rel="stylesheet" href="https://fred-wang.github.io/MathFonts/XITS/mathfonts.css"/>
<math>
<mrow>
<mo>{</mo>
<mfrac>
<mn>5</mn>
<mn>6</mn>
</mfrac>
<mo>)</mo>
</mrow>
<msqrt>
<mfrac>
<mn>3</mn>
<mn>4</mn>
</mfrac>
</msqrt>
<msub displaystyle="true">
<mo>∲</mo>
<mi>C</mi>
</msub>
</math>
<math dir="rtl">
<mrow>
<mo>{</mo>
<mfrac>
<mn>٥</mn>
<mn>٦</mn>
</mfrac>
<mo>)</mo>
</mrow>
<msqrt>
<mfrac>
<mn>٣</mn>
<mn>٤</mn>
</mfrac>
</msqrt>
<msub displaystyle="true">
<mo>∲</mo>
<mi>ج</mi>
</msub>
</math>
There are two cases when it comes to mirroring. If there is a corresponding mirrored character (e.g. opening parenthesis to closing parenthesis), it is called character-level mirroring or Unicode BiDi, and the browser just needs to swap one character for the other. Sadly, this doesn't apply to every operator.
Take the contour clockwise integral. If we just mirror the symbol by applying a reflection symmetry about a vertical line, the arrow is suddenly pointing in the other direction, making it counterclockwise. This changes the meaning of the formula!
To avoid this, the rtlm font feature can use glyph-level mirroring to provide a different set of correctly mirrored glyphs.
Glyphs plural since a math symbol can have different size variants to accommodate multiple contents.
Not only that, when the variants are not enough, there are glyphs for assembling arbitrarily long operators.
<link rel="stylesheet" href="https://fred-wang.github.io/MathFonts/XITS/mathfonts.css"/>
<math>
<msqrt>
<mspace height="0.8em" width="0.8em" style="background: tomato"></mspace>
</msqrt>
<msqrt>
<mspace height="1.5em" width="0.8em" style="background: gold"></mspace>
</msqrt>
<msqrt>
<mspace height="2.5em" width="0.8em" style="background: mediumseagreen"></mspace>
</msqrt>
<msqrt>
<mspace height="4.5em" width="0.8em" style="background: cornflowerblue"></mspace>
</msqrt>
</math>
<math dir="rtl">
<msqrt>
<mspace height="0.8em" width="0.8em" style="background: tomato"></mspace>
</msqrt>
<msqrt>
<mspace height="1.5em" width="0.8em" style="background: gold"></mspace>
</msqrt>
<msqrt>
<mspace height="2.5em" width="0.8em" style="background: mediumseagreen"></mspace>
</msqrt>
<msqrt>
<mspace height="4.5em" width="0.8em" style="background: cornflowerblue"></mspace>
</msqrt>
</math>
No browser engine supported glyph-level mirroring for MathML operators, so we had to implement it in all of them. Thankfully harfbuzz, the underlying font rendering library used by Chromium and Firefox, already supported it. WebKit is a work in progress, since there is more complexity because of different ports using different backends. As for character-level mirroring, Chromium and WebKit did it right, but Firefox applied reflection symmetry instead of replacing the correct pair. The changes in Firefox and Chromium are now stable and ready to be used!
| Feature | Firefox | WebKit | Chromium |
|---|---|---|---|
| Character level mirroring (BiDi) | ✅✨ | ✅ | ✅ |
| Glyph level mirroring (rtlm) | ✅✨ | 🚧 | ✅✨ |
math-shift and math-depth
Details are important, especially when rendering complex and layered formulas. One may think that a few pixels do not make that much of a difference. However, when you have multiple levels of nesting, offsets, and multiple elements, a slight change can make everything look ugly at best, wrong at worst.
Enter math-shift: compact. Look at this example from the MathML Core spec:
<math display="block">
<msqrt>
<msup>
<mi>x</mi>
<mn style="color: mediumseagreen">2</mn>
</msup>
</msqrt>
<mo>≠</mo>
<msup>
<mi>x</mi>
<mn style="color: cornflowerblue">2</mn>
</msup>
</math>
At first glance, you may not see anything too different. But looking closely, the green "2" on the left is a bit lower than then blue one on the right. It is trying to fit under the square root bar. This is what LaTeX calls cramped mode.
Chromium already supported the definition given by MathML Core, so that left Firefox and WebKit, both of which used hardcoded rules for specific cases in C++ objects. MathML Core takes another approach, and incentivizes using CSS styling rules instead.
Another interesting property is math-depth.
It is used to make nested elements, such as those inside fractions, scripts or radicals a bit smaller.
That way, if you have an exponent of an exponent of an exponent (of an exponent...), each one is displayed a bit tinier than the last.
<math display="block">
<msup>
<mi>A</mi>
<msup>
<mi style="color: cornflowerblue">A</mi>
<msup>
<mi style="color: mediumseagreen">A</mi>
<mi style="color: tomato">A</mi>
</msup>
</msup>
</msup>
<mo>+</mo>
<mroot>
<mi>A</mi>
<mi style="color: mediumseagreen">A</mi>
</mroot>
<mo>+</mo>
<mfrac>
<mrow>
<mi>A</mi>
<mo>+</mo>
<mfrac>
<mi style="color: cornflowerblue">A</mi>
<mi style="color: cornflowerblue">A</mi>
</mfrac>
</mrow>
<mi>A</mi>
</mfrac>
</math>
In this case, Firefox and Chromium already had compliant implementations, so only WebKit needed to catch up.
Support for math-depth and the scriptlevel attribute (which allows to modify this depth) has now landed,
while a patch for font-size: math (which sets the size of the element based on its depth) is on the way.
| Feature | Firefox | WebKit | Chromium |
|---|---|---|---|
math-shift: compact |
✅✨ | ✅✨ | ✅ |
math-depth |
✅ | ✅✨ | ✅ |
font-size: math |
✅ | 🚧 | ✅ |
scriptlevel |
✅ | ✅✨ | ✅ |
Other work
Rendering unknown elements as mrow
MathML 3 defined 195 elements.
MathML Core focuses on about 30, leaving the rest to styling or polyfills.
This means deprecating some features that were previously implemented in some browsers, like mfenced, semantics, and maction, as it would be too difficult to make them interoperable right now.
To prevent breaking existing content too much, they are rendered like an mrow.
font-family: math
Selecting a good math font is essential for rendering.
Stretchy operators, math symbols, and italics are not available with every font, so without one they are presented very poorly.
font-family: math is a CSS property that specifies that the content should use a suitable font for mathematics.
Previously browsers had a hardcoded list of CSS fallbacks, but now this has been standardized and implemented.
Android doesn't come with a math font installed, so it mixes symbols from different fonts, producing a rather unappealing result:

mathvariant and text-transform: math-auto
Single letter identifiers inside a <mi> tag are treated as variables, and so they should be rendered with fancy italics.
This is still supported by MathML Core.
However, MathML 3 allows a plethora of transformations using mathvariant, from bold to gothic text.
The new spec says that while italic transformation should still happen by default, other text should use the specific Unicode codepoint directly, as it just adds too much complexity for the browser implementation.
text-transform: math-auto is a CSS property applied by default to <mi> elements that enables the italic transformation for them.
Setting the new mathvariant attribute to normal will make the text-transform of the element be none, removing the italic styling.
DisplayOperatorMinHeight and Cambria Math
Microsoft made a mistake in Cambria Math, one of the math fonts used in Windows.
They switched the DisplayOperatorMinHeight and DelimitedSubFormulaMinHeight, so operators weren't being displayed correctly.
Some browsers had a workaround for this, but a more general fix was implemented in harfbuzz, so we removed the workarounds in favour of relying on the upstream library instead.
Animation for math-* properties
When implementing math-shift in Firefox, we noticed that the spec said the new properties are not supposed to be animatable.
In the new CSS spec, most properties are defined as animatable (fun!).
After some discussion with the MathML Working Group, we decided to change the spec, and we are adding this feature to the browser engines.
| Feature | Firefox | WebKit | Chromium |
|---|---|---|---|
Render unknown elements as mrow |
✅✨ | ✅✨ | ✅ |
font-family: math |
✅✨ | ✅✨ | ✅ |
text-transform: math-auto |
✅ | ✅✨ | ✅ |
New mathvariant behaviour |
✅ | 🚧 | ✅ |
DisplayOperatorMinHeight fix |
✅✨ | ✅✨ | ✅✨ |
Animation for math-* properties |
✅✨ | 🚧 | 🚧 |
What's next?
Many of these improvements have already shipped, but our work continues on making mathematics more interoperable in browsers. This includes some exciting new features ahead:
- Updates to the operator dictionary: MathML Core revamped the existing list of operators and their default layouts. Additionally, there is a new compact form that removes redundancies.
- More improvements to operator stretching and spacing: There are still some inconsistencies between browsers and some long standing bugs that we would love to tackle.
- Handling positioned elements and forbidding floats in MathML:
Like flex or grid, MathML doesn't create floating children for elements with a
mathdisplay type. However, they can still have out of flow positioned children. At the moment this isn't consistent across browsers and it is something we want to improve.
Working on MathML is very rewarding, specially because of the people that have helped along the way. I'd like to specially thank my colleague @fredw, reviewers from Mozilla, Apple and Google, and the W3C Math Working Group. Also @delan for reviewing the first draft of this post.
We are very grateful to the Sovereign Tech Fund for supporting this work!