DEV Community

Cover image for We built a free collaborative LaTeX editor. Here's the part that was actually hard.
letx app
letx app

Posted on • Originally published at Medium

We built a free collaborative LaTeX editor. Here's the part that was actually hard.

The deadline was nine hours out.

My co-author was in another timezone, editing the same main.tex I had open. We were passing the file back and forth over a shared drive like it was 2020. At some point we both hit save. A results section disappeared, and neither of us could say with confidence which copy was the real one.

That night didn't make me want to start a company. It made me want to throw my laptop in a river.

It's also why we built LetX — a free, real-time collaborative LaTeX editor that runs in the browser.

This post is about the engineering, not the pitch. Specifically: the part that took four times longer than we estimated, and why the obvious solution doesn't work.

First, the question every developer asks
"Why not just use git?"

We did. For a while it was great. Then it stopped being great, for a reason that has nothing to do with git being bad.

Git works when everyone editing the document understands git. In a research group, that's usually one or two people. The rest are statisticians, chemists, and supervisors who have a paper to write and no interest in learning what a rebase is.

I once watched a genuinely excellent statistician spend forty minutes resolving a merge conflict in a .bib file. That's not a workflow. That's a tax on someone whose time is worth considerably more than the tooling that consumed it.

There's also a structural mismatch. Git operates on line-level diffs, and LaTeX prose doesn't respect line boundaries. Reflow one paragraph and you've "changed" every line in it. Two people editing different sentences of the same paragraph produce a conflict git cannot resolve, because from git's point of view they rewrote the same line.

You can mitigate this — one sentence per line, latexdiff, semantic linefeeds. Every mitigation is a convention you now have to enforce socially across a group of people who did not agree to it.

Social solutions to technical problems don't survive contact with a deadline.

The actual hard part
Real-time collaborative editing is one of those problems that looks solved from the outside. Google Docs did it in 2006. There are libraries. How hard can it be.

Here's the shape of the problem. Two people type into the same document from different countries on different network conditions. Their edits arrive out of order. You need every participant to converge on an identical document, without a central referee deciding who wins, and without ever showing anyone a "someone else is editing, try again" dialog — which is just a merge conflict wearing a nicer outfit.

There are two established approaches.

Operational Transformation (OT) is what Google Docs uses. Every edit is an operation, and operations get transformed against concurrent ones before being applied. It works, and it's notoriously difficult to implement correctly — the transformation functions have to be right for every pair of operation types, and getting them subtly wrong produces divergence that only shows up under specific network timing.

CRDTs (Conflict-free Replicated Data Types) take a different approach: design the data structure so concurrent operations commute by construction. If operations commute, order stops mattering, and convergence is a property of the type rather than something you have to prove about your transformation logic.

We went with CRDTs, using Yjs. Same algorithm class Figma uses for multiplayer.

The tradeoff is honest: CRDTs carry metadata overhead. Each character effectively needs identity so the structure can order operations deterministically. Yjs is very good at compressing this, but "very good at compressing overhead" is not "no overhead."

For a 300-page thesis, that's a real consideration.

Why LaTeX makes this harder than it looks
Here's what a generic collaborative text editor doesn't have to worry about.

LaTeX is not prose. It's a markup language with strict structural requirements, and it fails loudly and completely on violations. One unmatched brace doesn't degrade the output — it kills the compile.

Consider two users editing this concurrently:

\begin{theorem}
Let $f: X \to Y$ be a continuous map.
\end{theorem}
User A wraps the statement in \textbf{...}. User B, at the same moment, edits inside the same line. Both edits are individually valid. The CRDT converges — that's guaranteed, that's the whole point of the type.

But converging on a consistent document and converging on a compilable document are different properties. The CRDT promises the first. It has no opinion whatsoever about the second.

Now scale that to environments:

\begin{align}
a &= b + c \
d &= e + f
\end{align}
If one user deletes \end{align} while another is typing inside the block, you get a document every client agrees on and no compiler will accept.

Generic text editors don't have this failure mode, because generic text doesn't have a compiler that rejects the entire file over a single missing token. A missing in HTML renders badly. A missing \end{align} in LaTeX produces nothing at all.

Reconciling CRDT convergence with LaTeX's structural requirements is where our estimate went wrong. Not by a little.

Estimates are like that.

The compile loop is a latency problem, not a throughput problem
Second architectural thing we got right early, mostly by accident.

Writing LaTeX is a loop:

edit → compile → look at the PDF → edit again
You run that loop hundreds of times a day. It's not a batch job — it's an interactive feedback cycle, and it should be measured like one.

This distinction matters more than it sounds. Optimizing for throughput means asking how many documents you can compile per minute across all users. Optimizing for latency means asking how long this user waits for this compile.

A queue is the clearest expression of the difference. Queuing is excellent for throughput and terrible for latency, and worse, it makes latency unpredictable — your compile time now depends on how many strangers happen to be compiling right now. Unpredictable delay breaks concentration in a way that consistent slowness doesn't. You can adapt to a compile that always takes three seconds. You can't adapt to one that takes one second or eleven depending on the time of day.

We target roughly a second, no queue. The interesting engineering question — how you isolate arbitrary user-supplied LaTeX, which can execute shell commands under the wrong configuration, while keeping cold-start low enough for interactive use — is a whole post on its own.

Note to author: dev.to readers will ask about compile sandboxing in the comments. Have a real answer ready — container per compile, gVisor, seccomp profile, -no-shell-escape, whatever you actually do. Don't hand-wave it. This is the single most likely technical question on this post.

Three constraints we set and refused to relax

  1. The free tier has to be genuinely usable, not a demo.

Unlimited projects, unlimited collaborators, unlimited compiles. No per-seat charge for adding your advisor or the undergrad doing the figures.

This is a real constraint on the business and we took it deliberately. Existing cloud editors solved collaboration and then metered it — compile timeouts, caps on how many people can be in a document, version history behind a paywall. Version history is the one feature you need most on the day something goes wrong.

The people who most need LaTeX to be free are the people most likely to hit the paywall. Students, early-career researchers, labs outside well-funded institutions.

As an engineering constraint, "unlimited compiles on the free tier" is a much more interesting problem than it sounds. It means compile cost per user has to be low enough that the free tier doesn't scale into a fire.

  1. Compiles are fast and unqueued. Covered above.

  2. Unpublished work stays private.

Researchers hand an editor unpublished results, grant applications, and blind-review submissions. That's not ordinary user data and it shouldn't be treated as ordinary user data.

What we got wrong
We assumed people would come for the collaboration. Collaboration was the entire thesis of the product.

What people actually asked for, repeatedly, was templates.

Not because they couldn't write a preamble — because starting is the expensive part. Nobody wants to spend the first two hours of a submission hunting for the right IEEE class file, or reverse-engineering a department thesis format from a PDF a senior student emailed them.

So we built the library out to 600+ templates: IEEE, ACM, Springer, Elsevier, theses, CVs, posters, grant applications.

The lesson generalized further than templates. We'd been optimizing the middle of the workflow. The friction was concentrated at the very beginning of it.

If you're building a tool, it's worth asking which part of the workflow you've actually instrumented. We were measuring the part we found interesting.

Where we are
1,300+ students and researchers, across 90+ universities and research labs in 35+ countries.

That's a real number and a small one. I still read every piece of feedback, and a single well-argued complaint can change the roadmap in a week.

Free, browser-based, nothing to install. Real-time collaborative editing, live PDF preview, version history with rollback, SyncTeX, and ZIP import for moving an existing project across.

Next up, both early: an agentic LaTeX editor that can refine a section or insert a table without you leaving the document — LaTeX is unusually well suited to this, because the source is text and the output is verifiable, so you can actually check whether the model's suggestion compiled. And QuantumSketch, which turns a paper into slide decks.

If you write LaTeX with other people
letx.app — free tier, no install, import an existing project as a ZIP.

If the compile loop doesn't feel faster, I'd genuinely like to know why.

And if you've built collaborative editing on CRDTs and hit the structural-validity problem from a different angle, I want to hear how you handled it. That's the part I'm still least satisfied with.

Top comments (0)