TL;DR: I built Scriba — a free, open-source (MIT) renderer that takes a LaTeX subset and turns a full problem statement — math, prose, tables, highlighted code and step-by-step algorithm animations — into one self-contained, sanitized HTML file. Animations are driven by real code, not hand-drawn frames. No signup: open the playground, paste, render.
Hello, Codeforces!
If you've ever written an editorial — for a school contest, a gym, or just a detailed solution comment — you know the drill. The prose and the math are the easy part. Then you get to "and now the DP table fills up like this", and suddenly you're hand-drawing cells in Excalidraw, screenshotting each state, numbering the PNGs, and pasting them in order. Then you find an off-by-one in your own explanation, and all nine screenshots are garbage. Re-draw, re-shoot, re-upload.
I did this one too many times while building the editorial corpus for my judge, and got tired of it. So I built something.
The friction with hand-made diagrams
- Screenshots go stale the moment the algorithm logic changes — fix a bug, redraw every frame.
- A GIF can't be paused mid-step, and readers can't step backwards.
- The diagram lives in a different tool than the statement, so they drift apart silently.
- KaTeX/MathJax solve the math beautifully, but an editorial is math + prose
- tables + code + "watch what happens at step 4".
- Sharing source with a co-setter means "here's the .tex, and also this Excalidraw link, and these 9 PNGs".
What Scriba does
- Full statements, not just math: math, prose, tables, and code highlighting in one document, rendered server-side (the math inside goes through KaTeX — credit where due).
- 21 animation primitives: array, DP table, matrix/heatmap, graph, tree, stack, queue, deque, linked list, hash map, number line, code panel, variable watch… most editorial diagrams map 1-1 to a shape you already think in.
- Frames driven by real code: a sandboxed Starlark block (
\compute/\foreach) runs your actual loop and fills frames from its output — fix the logic once, every frame updates. - One self-contained HTML file: everything inlines — math, styles, the tiny player script. Drop it into any page; no LaTeX toolchain or JS library to integrate on the client. Output is sanitized, so rendering statements from untrusted setters won't script-inject your site.
- Live playground with shareable snippet links — write, render, send a link.
- Free, MIT,
pip install scriba-tex.
What the source looks like
A minimal 3-step animation:
\begin{animation}[id="hello", label="Hello Scriba"]
\shape{a}{Array}{size=5, data=[3,1,4,1,5], label="my array"}
\step
\narrate{A simple array with 5 elements.}
\step
\recolor{a.cell[2]}{state=current}
\narrate{Highlight the middle element.}
\step
\recolor{a.cell[2]}{state=done}
\narrate{Mark it as done.}
\end{animation}
That renders as an inline player with play / pause / step controls.
GIF/video của player — nên quay bằng ví dụ matrix-chain bên dưới
The interesting part is when frames come from real logic. This is an actual example from the guide — the matrix-chain DP table, computed by the real nested loops inside a \compute block, then poured into the table:
\begin{animation}[id="matrix-chain", label="Matrix-chain DP table"]
\shape{dp}{DPTable}{rows=5, cols=5, label="dp[i][j]"}
\compute{
n = 5
p = [30, 35, 15, 5, 10, 20]
dp_vals = [[0 for _ in range(n)] for _ in range(n)]
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
dp_vals[i][j] = 10**9
for k in range(i, j):
cost = dp_vals[i][k] + dp_vals[k+1][j] + p[i] * p[k+1] * p[j+1]
if cost < dp_vals[i][j]:
dp_vals[i][j] = cost
}
\step
\foreach{i}{0..4}
\foreach{j}{0..4}
\apply{dp.cell[${i}][${j}]}{value=${dp_vals[i][j]}}
\endforeach
\endforeach
\narrate{Minimum matrix-chain multiplication cost table, built with nested for loops.}
\end{animation}
If I later realize the cost formula was wrong, I fix the loop — not nine screenshots.
How is this different from VisuAlgo / drawing by hand?
Visualizers like VisuAlgo are great for learning a canonical algorithm, but they animate their fixed examples — you can't feed them your problem's data or your transition. Scriba animations are part of your LaTeX source and run your logic. And compared to the Excalidraw/TikZ + screenshot workflow, the diagram can't drift out of sync with the statement, because they're the same file.
To be honest about scope: Scriba renders a subset of LaTeX (body content, not the preamble) plus its own animation syntax. It is not, and doesn't try to be, full LaTeX.
Battle-tested on a real corpus
Every editorial on my judge (app.judge.zone) is rendered with Scriba: 770 problems — 65 USACO contests (2011–2026) plus a curated 50-problem CSES batch spanning intro to advanced — with statements and editorials in 8 languages (including RTL Arabic), animations included. So the renderer has survived a real multilingual corpus, not just demos.
Even if you never write an editorial: the playground works as a free scratchpad — paste your own DP transition or graph trace while upsolving and step through it, instead of dry-running it on paper.
Try it
Open scriba.judge.zone — no signup. The LaTeX guide and animation guide cover every command with runnable examples. To render locally or self-host: pip install scriba-tex (v0.39.0), source on GitHub (MIT).
Feedback / bug reports / feature requests
Two concrete questions I'd love answers to:
- Next primitive: Trie, DSU forest, or Fenwick/BIT — which one would you actually use first?
- Which LaTeX command do you miss most inside statements?
Bug reports and PRs welcome on GitHub, or come argue about animation timing in the Discord: discord.gg/pg52FspEns.
Thanks
Scriba stands on KaTeX — all math inside statements is rendered through it server-side, and it's an absurdly good piece of software. Thanks also to every editorialist on CF whose hand-drawn DP tables and graph doodles over the years quietly defined the list of 21 primitives — this tool is basically those whiteboard drawings, made reproducible.
Happy animating.



