Render mathematical expressions in Markdown On GitHub
github.blog
github.blog
Too bad they're not using KaTeX [0] instead.
It renders the maths server-side, so there's no runtime needed.
An additional bonus is that the resulting math is copy-pasteable, which in the case of disply math might not be that useful (since most equations are to complex to be meaningfully copy-pasted with unicode), but it helps from inline math dissappearing when copy pasting texts.
But, that being said, I'm sure they had their reasons to do so. For one, MathJax seems more well-known by quite a bit so maybe it's the safer option.
Original page (compressed): 10 kB
Page with server-rendered Katex (compressed): 50 kB
Katex.js (compressed): 80 kB
So after two pages it’s a net win to not render the mathematics server-side.
Running the JS client side, like GitHub, means blocking the thread. You're either going to be 1. delaying other JS from running, or 2. rendering late, shifting the layout – which is what GitHub has chosen: https://imgur.com/a/y47haf9
(Sending the rendered div's is a non-starter. Large document sizes delay domContentLoaded, slow down browsers, aren't shared cacheable resources, etc.)
Your approach, then. On your page there are 178 SVGs. Total gzipped size is 490KB. SVGO[0] gets that down to 311KB – that's 1.74KB transferred per equation. These are non-blocking, immutable, cacheable assets. Brilliant.
Upgrades:
- Figure out viewbox and inline height/width values on the HTML so no layout jank (aka "Cumulative Layout Shift"/CLS) occurs. Unsure if this is possible for inline math.
- Add `loading="lazy"` afterwards. Users that don't scroll the entire length of the page won't suffer unneeded downloads, and the inlined sizes will prevent late CLS for those that do.
- Maybe re-add interactivity? Cheap option: just support copying alt-text in a context menu.
That's a great idea, I should do that. Right now I apply the height and offsets from the SVG file (via vertical-align and height) so that it flows nicely but I should add width too. It is trivially doable since the SVG does contain the width.
e.g.
<img src="//daniel.lawrence.lu/texcache/683524188b1547a2a4466541ff07f2d6f83f599bi.svg" alt="\mathbf R(\mathbf T)" style="vertical-align: -0.838ex;height:2.843ex;">They do, accessibility being one of them. Rendering math in the way you describe makes it difficult, or impossible, to understand for all sorts of audiences, including those relying on screen reading software.
However, isn’t that just the fact the the default math font (DejaVu Math TeX Gyre in Firefox i-on Ubuntu) is not good enough? I use Libertinus Math in my own little demo page (https://runarberg.github.io/mathup/) and it looks just fine (IMO). The only think to note is that TeX Gyre is so pervasive (as it is the default [only?] MathJax font) that it takes a bit getting used to other math fonts on the web.
For example here is the Iglalia example from your demo page displayed with Libertinus Math in Firfox (https://imgur.com/q3QuJOA). The same example has some issues in your demo page with DejaVu Math TeX Gyre
I don't think I'd qualify an issue from the end of last year as "unfixed for a long time".
By offloading the rendering to the client, you make use of the spare capacity that exists on most client machines today, with no noticable slowdown for the user (it may even end up doing the "first paint" faster in the browser if GitHub are careful about their implementation).
Even with my SaaS product, we try and do as much work on the client as possible to reduce our server requirements. If you're sensible about it, users don't even notice.
You're right on the money with the server costs though.
The JS interpreter is single threaded. But, you can offload the work to a web worker, that runs another instance of the interpreter in a separate thread.
Also for rendering LaTex it’s possible to optimize the rendering algorithm using WASM.
I’m not saying that MathJax does that, or it has good performance. But, there are options to optimize the client side rendering.
I don't know enough about the state of WASM today to say the same. Circa 2018 I know that it lacked DOM manipulation.
(In some distant year I will figure out the incantation to avoid getting sniped when talking about this stuff. I think I rewrote that line 4 times.)
Because we have to participate in the main thread either way, prerendered images will always paint faster. There is simply too much action in the thread during the initial GitHub page lifecycle. The 40-50ms (~one long task) cost of spinning up a new web worker just solidifies that.
Have you visited math.stackexchange? Pop by their MathJax reference page[0], and observe how long all the mathematical notation takes to render fully—it takes at least six seconds on my recent notebook, plugged in. On my 2018 iPad Pro, it takes well over thirty seconds on the first page load (drops to ~5 s on subsequent visits: there's probably some caching going on).
Here's[1] a benchmark comparing KaTeX (server-side), MathJax 2.7, and MathJax 3.0 (apparently a complete rewrite supporting server-side rendering[2], but it's still noticeably slower than KaTeX).
MathJax is really slow (slower still than LaTeX itself, and that's saying something).
[0]: https://math.meta.stackexchange.com/questions/5020/mathjax-b...
[1]: https://www.intmath.com/cg5/katex-mathjax-comparison.php
[2]: https://docs.mathjax.org/en/latest/upgrading/whats-new-3.0.h...
For example, I see no reason why they wouldn't just render the stuff that's in view first, and the rest is rendered incrementally as the user scrolls, or a few seconds after the initial paint.
I take your point that KaTeX is faster in absolute terms, but from a usability point of view, the benefits do not outweigh the costs in my opinion.
Did you wait for the typefaces to change to Computer Modern? On my smartphone, this took ages, and that is what I implied by 'fully loaded'; more complex maths is unreadable on some devices with only the native typefaces. Even a (relatively straightforward) cube root doesn't look clean[0].
MathJax has (from my observation) two 'levels' of rendering, where it uses the OS native typeface for a first pass, and then renders everything in Computer Modern for a uniform look (except on Apple devices, which somehow override this with STIX fonts).
Needless to say, the typeface change means that the dimensions of the maths content change, causing the rest of the website to reflow a second time. This second render takes the bulk of the time, and having a website's content unexpectedly jump around a long while after it has (apparently) loaded is not exactly user-friendly.
Does this really matter though? When I've encountered math-heavy pages that have been slow to fully render the math has also been slow for my brain to processes. As long as the math rendering is faster than my brain's math understanding it has been fine.
Why?
Because markdown files seldom change and probably read much more often.
Anyway I would not agree that it is a good idea to move everything to client just because you can. There are always up and downsides.
I think your comment is too generic.
I don't know what your SAAS does (maybe it's not really applicable here), but in the case of github, it's very wastefull. Yes, it would take some work, some servers, etc but they have the skill, the money and everything they need to do it. They could do it once (well, with every change, but it's not very often) on their side instead every client on every pageview will have to do the same work again and again, wasting time and energy.
Do you find all native applications rude to customers?
Also there are some minor annoyances. For example you need to escape the backslashes, so it takes 4 backslashes to make a newline in your matrix.
$$\begin{bmatrix} a & b \\\\ c & d\end{bmatrix}$$
I found that the inline math doesn't quite match the font size of the surrounding text too (it's smaller).> $$\begin{bmatrix} a & b \\\\ c & d\end{bmatrix}$$
This example doesn't make sense. You need to escape the backslashes in the end-of-row command \\, but you don't need to escape the backslashes in \begin and \end?
The post clearly shows that backslashes in \left, \right, \sum, and \sqrt do not need to be escaped. What's different about \\?
That said, \\begin renders the same as \begin.
As a data point: I have the same experience using MathJax in my own blog (Hugo, Goldmark markdown engine), so I'm assuming there's some structural reason for it, not blaming Github.
## Example Matrix
$$$
[a, b; c, d]
$$$
Although I realize that we are probably stuck with LaTeX for the foreseeable future as the only way to type math in text files.Disclaimer, I am an author of an alternative math markup language specifically designed to be a good fit in Markdown.
What's your language? Any plans to build some sort of translator or other mechanism to allow high-quality rendering based on it?
obrace(a\`↑↑`b = ubrace(a^a^⋰^a)._(b "times")).^"" "up-arrow" notation ""
I don’t think you’ll be able to read this without knowing some of the syntax... which is a failure on my part as author. `obrace` and ubrace` are clear `.^` puts the following expression (a text that works the same way as backticks in markdown) over the preceding expression. `._` does the same but puts it under. and the backslash will make the thing surrounded by backticks (\`↑↑`) an operator. But this is a fairly complected expression. And my goal was never to make every expression look simple. A far more common expression would be easier: a^n = obrace(a xx a xx cdots xx a).^(n "times")
which could also be written as: a^n = (a × a × ⋯ × a).^⏞.^(n "times")
Regarding cases, in mathup could write: n! = { 1, if n <= 1
(n-1)!, otherwise
However the alignment won’t be perfect... I was always going to go back and fix that, but I never got around to do that.Other improvements include, using white space smartly to group things together e.g. (This example also showcases using slash to denote fraction)
a/b + c/d != a / b+c / dThe 'default MathJax font' is also just the 'default LaTeX font' (CM?). There are countless other, high-quality math fonts, for example the TeX Gyre Math fonts. I'd like to see those one day, I prefer them. It would be not more out of place than the current version.
There are font pairs, too, like TeX Gyre Pagella and its TeX Gyre Pagella Math accompanying font. So you have the same font for both, which is simply beautiful and even more readable I'd argue. I think most people have accepted that text and math fonts are always distinct, when in fact they needn't be.
They work brilliantly together as well, and in my opinion, a little better fit for the web then TeX Gyre—which is optimized for the printed page.
You can see an example on the doc for my project Mathup: https://runarberg.github.io/mathup/ (you might need firefox or safari to see the font though, as you need the native MathML to render the equations)
[1]: http://docs.mathjax.org/en/latest/output/fonts.html [2]: https://github.com/mathjax/MathJax/issues/2503#issuecomment-... [3]: https://github.com/mathjax/MathJax/issues/2503#issuecomment-...
https://github.github.com/gfm/
This clue indicates that it might become really annoying to talk about actual dollar signs in some cases:
https://docs.github.com/en/get-started/writing-on-github/wor...
“Outside a math expression, but on the same line, use span tags around the explicit $.
To split <span>$</span>100 in half, we calculate $100/2$”
OK:
$$\|\vec{x} - \vec{p}_c\| = v(t_c-t_0)$$
--
NOT OK:
$$
\|\vec{x} - \vec{p}_c\|
= v(t_c-t_0)
$$
This makes larger latex expressions pretty impractical e.g. https://github.com/jurasofish/multilateration/blob/master/re...
$$ \ce{ \underset{global coordinate}{(x,y,z)} ->[\mathcal{R}][rotation] \underset{local coordinate}{(x',y',z')} -> \underset{descriptor}{\{\mathcal{D}_{ij}\} } -> E_i } $$
There does appear to be a bug though where the LaTeX expressions don't render if they're in markdown lists or tables.
I'd expect a syntax more like OpenOffice math.
https://wiki.openoffice.org/wiki/Documentation/OOoAuthors_Us...
[0]: https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/doc/... [1]: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8...
Pluging project here if anyone is interested: https://github.com/atonalfreerider/riemann-zeta-visualizatio...
https://github.com/github/feedback/discussions/categories/ge...
Unfortunately they don't work on GitHub Pages yet. I still have to resort to GitHub Actions to invoke a container to render my RMarkdowns into a GitHub Pages branch --- wish one day I can retire that pipeline.
> $$left( \sum_{k=1}^n a_k b_k \right)^2 \leq left( \sum_{k=1}^n a_k^2 \right) left( \sum_{k=1}^n b_k^2 \right)$$
I wonder why their syntax highlighting on this example highlights `left` but not `right`? (I can't seem to reproduce it here, since the HN parser tries to outsmart me.)
For example, I cannot find a “left” attribute here: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
https://github.com/DaveJarvis/keenwrite/blob/master/docs/scr...
The choice of using TeX over LaTeX is deliberate. Documents written using plain TeX can be rendered by either ConTeXt or LaTeX. I prefer ConTeXt because it makes separating content from presentation easier. One of the benefits of using Markdown, IMO, is to be agnostic as to how the documents are presented. Math could be rendered using ConTeXt, LaTeX, KaTeX, MathJax, JMathTeX, XeTeX, or any other compatible TeX typesetter.