Formulas and subject visuals¶
Lecture Studio draws the figures courses keep redrawing by hand: formulas, arrays, matrices, charts, neural networks, trees, graphs, memory diagrams and database schemas. Each one is a deck method, a model class and a JSON "type", like every other component.
Every visual is laid out in Python and rendered as plain HTML, MathML or inline SVG. There is no JavaScript and no download. The same input always produces the same figure, and figures stay sharp when zoomed or printed. Themes color them through --ls-viz-* tokens. Text inside a figure is escaped like all other author text.
Method |
JSON |
Draws |
|---|---|---|
|
|
Display formulas in LaTeX, with a legend of symbols |
|
|
A list as indexed cells, with pointers, a slice and negative indexes |
|
|
A grid, a math matrix ( |
|
|
A classifier’s confusion matrix with its accuracy |
|
|
Line, bar or scatter charts |
|
|
A fully connected neural network |
|
|
Search trees, parse trees, decision trees |
|
|
Nodes and edges, directed or not, with weights |
|
|
Call frames, variables and the objects they reference |
|
|
Database tables, keys and relationships |
All visuals accept title, description, caption, notes, id and show. A complete example for every visual is in examples/subjects.py.
Formulas¶
deck.math(r"\sigma(z) = \frac{1}{1 + e^{-z}}",
title="The logistic function",
legend={"z": "weighted sum", r"\sigma": "activation"})
deck.math([r"\hat{y} = \operatorname{softmax}(W\mathbf{x} + b)",
r"\theta \gets \theta - \eta \, \nabla_\theta J(\theta)"])
Formulas inside any text use $...$: "We compute $z = w^T x + b$ first." The rules follow Pandoc, so prices stay text. The opening $ must touch the formula and the closing $ must not be followed by a digit, which keeps "$5 to $10" as plain text. Write \$ for a literal dollar sign. Inline code (`$x$`) is never treated as a formula.
Formulas become MathML, which Chrome, Edge, Firefox and Safari render natively, and PDF exports keep them as selectable text. The supported LaTeX covers:
fractions and binomials:
\frac,\dfrac,\binom;roots and scripts:
\sqrt,\sqrt[n],^,_and primes;large operators and limits:
\sum,\prod,\intand\lim,\max,\min,\argmax;function names:
\log,\sin,\expand\operatorname{...};symbols: Greek letters, relations, arrows, set symbols;
accents:
\hat,\bar,\vec,\tilde,\dot;fonts and text:
\mathbf,\mathbb,\mathcal,\mathrm,\text;delimiters and environments:
\left/\rightandmatrix,pmatrix,bmatrix,vmatrix,cases,aligned.
An unsupported command raises ValueError when the lecture is built, never in front of students. to_mathml(latex) converts a formula on its own.
Arrays¶
deck.array([12, 7, 25, 3, 18, 9], name="nums", highlight=[2],
pointers={"low": 0, "mid": 2, "high": 5}, slice=[1, 4], negative=True)
pointers maps labels to indexes; an index equal to the length marks the position after the last item. slice=[start, stop] underlines nums[start:stop]. negative=True adds Python’s negative indexes.
Matrices, heatmaps and confusion matrices¶
deck.matrix([[1, 0, 2], [-1, 3, 1]], name="W", brackets=True)
deck.matrix(attention, row_labels=tokens, col_labels=tokens, heatmap=True)
deck.confusion_matrix([[48, 2, 0], [5, 40, 5], [1, 3, 46]], ["cat", "dog", "bird"])
highlight=[[row, col], ...] outlines cells and metrics={"F1": "0.91"} adds figures under the grid. confusion_matrix labels the axes in the lecture’s language, outlines the diagonal and computes the accuracy.
Charts¶
deck.chart({"training": [0.92, 0.61, 0.44, 0.35], "validation": [0.95, 0.66, 0.52, 0.49]},
labels=["1", "2", "3", "4"], x_label="Epoch", y_label="Loss")
deck.chart({"Python": [68, 74], "C++": [55, 61]}, mode="bar", labels=["2024", "2025"])
deck.chart([{"name": "samples", "values": [1.2, 2.3, 2.9], "x": [1, 2, 3]}], mode="scatter")
series is a list of numbers, a dictionary of named series or a list of {"name", "values", "x"}. Bar charts always include zero. Set y_min/y_max to fix the axis.
Neural networks¶
deck.network([784, 128, 64, 10], labels=["Pixels", "Hidden 1", "Hidden 2", "Digits"])
Layers wider than seven neurons are drawn as three neurons, a vertical ellipsis and three more, with the real size underneath.
Trees and graphs¶
deck.tree({"value": 8, "children": [
{"value": 3, "children": [{"value": 1}, {"value": 6}]},
{"value": 10, "children": [None, {"value": 14, "selected": True}]},
]})
deck.graph(["A", "B", "C", "D"], [("A", "B", 4), ("A", "C", 2), ("C", "B", 1), ("B", "D", 5)], directed=True)
A None child keeps an empty slot, so binary trees keep their left and right sides. Graph nodes are placed on a circle unless every node has x and y on a 0–100 grid. Edges are (source, target) or (source, target, label), or dictionaries with selected=True to highlight a path.
Memory diagrams¶
deck.memory({"global": {"scores": "@a", "alias": "@a"}, "average(values)": {"values": "@a"}},
{"a": [70, 80, 85]})
deck.memory({"main()": {"head": "@n1"}}, [
{"id": "n1", "type": "Node", "value": {"data": 5, "next": "@n2"}},
{"id": "n2", "type": "Node", "value": {"data": 8, "next": "nullptr"}},
], heap_label="Heap")
A value written "@id" draws an arrow to the object with that id. Objects hold text, a list (indexed slots) or a dictionary (named fields), so the same component shows Python names and objects, C and C++ pointers, linked lists and recursion. The last frame is drawn as the active one.
Database schemas¶
deck.schema({
"students": ["id INT PK", "name VARCHAR", "major_id INT FK -> majors.id"],
"majors": ["id INT PK", "name VARCHAR UQ"],
})
Each column is a shorthand string, name [TYPE] [PK] [FK] [UQ] [-> table.column], or a dictionary with name, type, key and references. Relationships are drawn from the foreign key, with the crow’s foot on the “many” side, to the referenced column. per_row sets how many tables share a row.
Syntax highlighting for every course¶
Python, C, C++, Java, C#, JavaScript, TypeScript, Kotlin, Go, Rust, Dart, Swift, SQL, Bash, R, MATLAB, JSON, HTML/XML and CSS are highlighted by the library itself, so they look the same on every computer and need no extra package. The code toolbar names the language (cpp shows as C++). Other languages use Pygments when the highlight extra is installed.
Code fonts never merge symbols into ligatures, so !=, >= and << look exactly as students type them.
Add a language or replace a built-in one with a function that returns escaped HTML using the same token classes:
import html
import re
from lecture_studio import register_highlighter
def pseudocode(code: str) -> str:
return re.sub(r"\b(IF|THEN|ELSE|WHILE|RETURN)\b", r'<span class="k">\1</span>', html.escape(code))
register_highlighter(["pseudo"], pseudocode, label="Pseudocode")