fxTeXServer
fxTeXServer is the same LaTeX→MathML converter as fxTeX, run on your side of the wire instead of in the reader's browser. It is written in C++ and ported entry-for-entry to TypeScript; the two are held byte-identical across a corpus of 15,367 equations, and every change lands in both before it is committed. So a page rendered server-side and the same page rendered client-side produce the same MathML, character for character — which is what makes it safe to mix the two.
It does not render images and it does not run TeX. It converts LaTeX to MathML, very quickly, with no I/O of any kind. If you need a PNG, an SVG or a PDF, that is LaTeXRender.
Why convert on the server
The page arrives finished. MathML in the HTML means no script has to run before the equations are right — no layout shift, nothing for a reader on a slow connection to watch happen, and nothing that breaks if JavaScript is off.
Search engines and feed readers see the maths. A crawler that does not run your JavaScript still gets the equation.
It happens once, not once per reader. Content converted at publish time is converted once for a million views.
You may not be producing a web page at all. An EPUB, a Word document, an email, a print pipeline — all of them want MathML, and none of them have a browser to produce it.
Client and server are not exclusive. The common arrangement is fxTeXServer at publish time for the page's own content, and fxTeX in the page for anything added afterwards — comments, a preview pane, a single-page application's later routes. Because the two produce identical output, nothing gives away which equation came from which.
Choosing a deployment
| Route | Best when | Shape |
|---|---|---|
| Node service | Your stack is already JavaScript, or you want conversion behind an HTTP call you control. | An npm package. Use it as a library, or run the service it ships with. |
| Linked library | Conversion has to happen inside an existing C++, PHP or Python application, with no network hop at all. | A compiled library and a header, linked into your own build. |
| Docker stack | You want an HTTP endpoint on your own infrastructure and would rather not build anything. | One image, one command, no persistent state. |
Conversion is pure and fast — a recursive-descent parse with no I/O, no cache to warm and no database. Whichever route you pick, there is no state to lose and scaling is a matter of adding replicas.
Node service
The TypeScript port runs unchanged under Node: the browser bundle guards its one piece of DOM
work on document existing, so it imports cleanly with no document in
sight. Used as a library, it is one call:
import { convertToMathML } from '@codecogs/fxtex';
const html = article.replace(/\$([^$]+)\$/g, (_, latex) => convertToMathML(latex));
Or behind an endpoint, if the rest of your stack is not JavaScript and you would rather call than link:
import express from 'express';
import { convertToMathML } from '@codecogs/fxtex';
const app = express();
app.use(express.json());
app.post('/convert', (req, res) => {
try {
res.type('application/mathml+xml')
.send(convertToMathML(req.body.latex, req.body.display === true));
} catch (err) {
res.status(422).json({ error: String(err) });
}
});
app.listen(8080);
Conversion is synchronous and takes microseconds, so there is no queue to manage and no reason to make the endpoint asynchronous. The one cost worth knowing about is construction: building the converter's symbol table is several milliseconds of one-time work, so build it once at startup and keep it — which the package does for you, at import.
Licence
The package is served from a private registry. Your licence gives you a read token for it and a key for the running service:
npm config set //registry.codecogs.com/:_authToken <your token>
npm install @codecogs/fxtex
export FXTEX_LICENCE=<your licence key>
The key is read from the environment at startup. Put it in your orchestrator's secret store, not in a file you commit and not in an image.
Linking into an existing application
When the conversion has to happen inside a process you already have — a PHP application rendering a page, a Python pipeline building EPUBs, a C++ service of your own — a network hop for a microsecond of parsing is the wrong shape. We supply the converter compiled, with its header, to link directly.
C++
One class, one call. Construct once and reuse it; the object is cheap to keep and there is nothing to tear down.
#include "Symbols.h"
Symbols symbols;
symbols.init();
std::string mathml = symbols.Latex2MathML("\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}");
Two members are worth knowing:
| Member | Meaning |
|---|---|
isInline | Set by the converter from the equation's own front matter — \inline makes an equation inline rather than a centred block. Readable after the call. |
errors | Commands the parser did not recognise in the most recent call, cleared at the start of each one. An empty vector means the equation converted cleanly. This is the hook to log against: it is how you find out which of your equations use something we do not yet cover. |
The object is not thread-safe — it carries parse state between its own internal calls. Give each thread its own, which costs nothing.
PHP and Python
The same library ships with a C entry point, which both languages can call without an extension to compile:
<?php
$fxtex = FFI::cdef(
"char* fxtex_convert(const char* latex); void fxtex_free(char* s);",
"libfxtex.so"
);
$mathml = FFI::string($fxtex->fxtex_convert('\frac{1}{2}'));
from ctypes import CDLL, c_char_p
fxtex = CDLL("libfxtex.so")
fxtex.fxtex_convert.restype = c_char_p
mathml = fxtex.fxtex_convert(b"\\frac{1}{2}").decode()
Licence
A linked library runs entirely inside your process, with no network and nothing to check against. There is no key: the licence is the agreement under which the library is supplied, and it is per-deployment. If you need the converter in a product you ship to your own customers, say so — that is a redistribution licence and it is a different conversation.
Docker deployment
An HTTP endpoint on your own infrastructure, with nothing to build. The image is private, so
the registry credential has to travel with the deploy — that is what
--with-registry-auth does, and without it the tasks never start because no node can
pull the image.
docker login
docker stack deploy --with-registry-auth --compose-file docker-fxTex.yml eqn
Then check it answers:
docker stack services eqn
curl "http://localhost:8090/?x^2"
docker-fxTex.yml
Short, because there is nothing to persist. No cache, no database, no volumes — a converter with no I/O has no state worth keeping between requests, which also means a replica can be killed mid-flight and nothing is lost.
version: "3.9"
services:
fxtex:
image: willzyba/fxtexserver:1.0.0
deploy:
mode: replicated
replicas: 4
placement:
max_replicas_per_node: 2
restart_policy:
condition: any
delay: 5s
max_attempts: 5
window: 120s
environment:
- FXTEX_LICENCE=<your licence key>
- FXTEX_POD_NAME=Docker-{{.Task.ID}}
ports:
- 8090:80
healthcheck:
test: wget -qO - http://localhost/health || exit 1
interval: 60s
timeout: 15s
retries: 3
start_period: 15s
Scaling is replica count and nothing else: every request is independent, CPU-bound and over in
microseconds. max_replicas_per_node keeps a single node's failure from taking the
whole service with it.
Sitting it behind your existing cache is worthwhile if the same equations recur — a textbook's worth of content converted on every page view is work you only needed to do once — but it is a convenience, not a requirement. Converting from cold is fast enough that most deployments never bother.
More
The HTTP interface, the query-string escaping it expects and what comes back are on the API page. For licensing, talk to us.
CodeCogs®