This folder contains marimo notebooks that explain concepts visually and
interactively. These notebooks exist because text-only explanations were
insufficient. If you’re writing text that could be a diagram, you’ve
already failed.
Rules for all notebooks
Draw first, label everything, explain last
Every concept gets a diagram. If you mention “height”, draw the
height line. If you mention “angle θ”, draw the arc and label it θ.
If you mention “projection”, draw the original shape, draw the
projected shape, and draw the projection lines connecting them.
Label every element in the diagram. Vectors get names and
component values. Lengths get formulas. Angles get symbols. Colors
in the legend match colors in the plot. No unlabeled geometry.
Markdown text is captions, not the explanation. A paragraph
should say “Notice that…” pointing at something already visible
in the diagram above. If the reader has to imagine what you’re
describing, the notebook is broken.
Light theme — mandatory
The marimo user config is set to theme = "light". All notebooks must
use a white / light background for all visualizations:
matplotlib: white facecolor on figure and axes. Use dark text,
dark axis labels, dark titles. No #1a1a2e dark backgrounds.
graphviz: bgcolor="white" on the digraph. Use standard system
fonts (Helvetica) — don’t assume JetBrains Mono or Inter are
installed in all environments.
plotly: use template="plotly_white" (not plotly_dark).
altair: default light theme (no override needed).
Technical conventions
Use marimo run (app mode) as the primary way to view. All cells
should have hide_code=True.
Use uv inline script metadata for dependencies. No pyproject.toml
per notebook — keep them self-contained.
Prefix cell-local variables with _ (e.g. _fig, _svg) — marimo
requires unique variable names across cells.
mise.toml at the code root defines run targets.
Math & interactive notebooks
For notebooks that compute something the reader interacts with (formulas,
simulations, parameter exploration).
Sympy for all formulas
Use sympy in every math notebook. Whenever a notebook presents a
formula, derivation, or symbolic result, render it with sympy rather than
writing raw LaTeX strings in markdown. Sympy gives you:
Beautifully typeset formulas via sympy.init_printing() or
mo.md(f"${sympy.latex(expr)}$")
Symbolic manipulation (simplify, expand, substitute, solve) so the
reader sees the math being computed, not just displayed
Step-by-step derivations: show each algebraic step as a sympy
transformation, not a wall of LaTeX
Pattern for showing a derivation:
import sympy as spx, y = sp.symbols("x y")expr = (x + y)**2expanded = sp.expand(expr)mo.md(f"""**Start:** ${sp.latex(expr)}$**Expand:** ${sp.latex(expanded)}$""")
Only fall back to raw LaTeX in mo.md() when there is no symbolic
computation involved (e.g., a purely textual reference to a formula).
Diagram-specific patterns
“Base × height”: Draw the base, draw the perpendicular height as
a dashed line, label both with their formulas, shade the resulting
area. Show the angle with an arc.
“Projection / shadow”: Draw the 3D object. Draw dashed vertical
lines from each vertex down to the target plane. Draw the projected
shape on the plane. Label the area of both shapes.
“These two things are equal”: Compute both numerically and show
them side by side. A table of numbers is fine as a supplement, but
the primary evidence should be visual (overlapping curves, matching
bars, etc.).
Library choices
Use matplotlib for 2D annotated diagrams (arcs, dashed lines,
annotations with arrows). Plotly is better for interactive 3D.
Use altair when the chart benefits from interactivity (selections,
tooltips, cross-filtering). Good for yield curves, vol surfaces,
correlation heatmaps, time series.
Use graphviz (Python bindings) for block/memory/architecture
diagrams. Render SVG via g.pipe(format="svg").decode("utf-8") and
embed with mo.md(f"...{_svg}...").
vegafusion scales altair to larger datasets via server-side
aggregation.
Figure presentation
Center all figures. Wrap mo.as_html(fig) in a centering helper:
Define centered_fig in the init cell and export it. Never use bare
mo.as_html(fig) in a vstack — it renders left-aligned with no spacing.
Add vertical spacing between elements in mo.vstack() calls.
The centered_fig helper includes margin-top: 1.5rem. For other
elements (tables, markdown), add spacing via wrapper <div> or
gap parameter if available.
mo.ui.dropdown value must be the display label (dict key), not
the option value. Using value="gauge" when the key is
"Slowly changing gauge (...)" silently crashes the cell.
Concept explanation notebooks
For notebooks that explain non-mathematical concepts (programming language
features, systems topics, architecture patterns) using code snippets,
text, and pre-rendered diagrams.
Embedded code snippets
WASM export bundles only the .py file — sibling files are not included.
Embed code snippets directly in the notebook using tagged regions:
_SNIPPETS_TEXT = _tw.dedent("""\ // tag::example[] let x = 42; println!("{x}"); // end::example[]""")def read_snippet(tag: str) -> str: pattern = rf"// tag::{re.escape(tag)}\[\]\n(.*?)// end::{re.escape(tag)}\[\]" m = re.search(pattern, _SNIPPETS_TEXT, re.DOTALL) return m.group(1).rstrip("\n") if m else f"// snippet '{tag}' not found"
Pre-rendered diagrams
C-extension libraries (matplotlib, numpy, scipy, graphviz) cannot run
inside WASM. For concept notebooks, pre-render diagrams offline and load
the output at runtime:
Create render_diagrams.py in the notebook directory — a standalone
uv-runnable script that generates SVGs into static/.
Load SVGs via a dual-mode helper in the notebook’s init cell:
This reads from the filesystem locally (marimo run) and falls back to
<img> tags in WASM export. build.sh copies static/ into the
published site.
WASM indentation helper
WASM export re-indents all cell source by 4 spaces. Triple-quoted strings
at column 0 lose their relative indentation. Use a dedent_md() helper:
def dedent_md(s): return mo.md(_tw.dedent(s))
Then indent all triple-quoted content to 4 spaces in source:
dedent_md(f""" ### Section title Explanation text here. ```rust {read_snippet('example')} ```""")
WASM pitfalls
Three bugs that cost hours during development:
_ prefix = cell-local in marimo. Variables starting with _ are
invisible to other cells. Never use _ prefix for helpers shared
between cells. Use dedent_md, not _md. Use load_svg, not
_load_svg. Cell-local temporaries like _fig or _code are fine.
WASM re-indentation. The export process re-indents cell source to
function-body level (4 spaces). Content in triple-quoted strings at
column 0 gets flattened. Always indent content and textwrap.dedent()
at runtime.
WASM bundles only the .py file. No sibling files ship — not
images, not .rs files, not other .py modules. Embed everything
inline or load from static/ via <img> tags.