Live site: https://pupitra.org/
Pupitra, from the French pupitre — the lectern. Not a course companion: the professor's bench. (The default seed stays 34, a surviving nod to the GR 34, the Breton coastal trail the project was first named after — a great many short, well-marked stages, walked one at a time and always in the same order, which is what a catalogue of lecture demonstrations is.)
A purely static scientific demonstration engine for teaching — a catalog of interactive experiments (statistics, estimation, detection, digital communications) that are scriptable, projectable, and drivable from the keyboard. Not a course companion: the professor's bench.
Every lecture scene is a URL: one link reproduces the exact experiment,
parameters, view and seed. Every experiment ships with a numerical
correctness harness (npm run check) that gates deployment. The default
seed is 34, for the same reason as the name.
See CLAUDE.md for the full vision, contracts and architecture.
npm install
npm run dev # local dev server
npm run check # numerical correctness harness (every experiments/**/check.js)
npm run build # static build in dist/, deployable as-is
npm run new:experiment # scaffold a new experiment (4 files, runs immediately)Pushes to main are built and deployed by Cloudflare's Git integration
(npm run check && npm run build, then npx wrangler deploy reading
wrangler.jsonc) and served at pupitra.org. The
check harness gates every deploy: a red check means no release.
| Key | Action |
|---|---|
⌘K / Ctrl+K |
Command Palette (experiment search) |
⌘B / Ctrl+B |
Toggle sidebar |
P |
Parameter drawer |
R |
Draw again (randomizeSeed) |
F |
Freeze frame — pin the current plot as a gray ghost, compare |
L |
Presentation mode (fullscreen, enlarged strokes/type) |
← / → |
Previous / next lecture scene |
Esc |
Clear ghost, close popover / palette, exit fullscreen |
Any scene becomes a live, drivable figure inside a Moodle page, an LMS
activity or any HTML document — add embed=1 to its URL:
<iframe src="https://pupitra.org/#/spectral/subspace?df=0.5&embed=1"
title="pupitra — High-resolution methods" loading="lazy"
style="width:100%; height:620px; border:none"></iframe>The frame keeps the tabs, the plot, the draw/freeze actions and the parameter
pills — students adjust the figure inside your page — and drops the rest of
the chrome. A small pupitra chip opens the same scene in the full catalogue.
Keep loading="lazy": the browser then only instantiates the frames near the
viewport. Measured with ten embeds on one page: the bundle is fetched once
(~320 kB gzip, shared by every frame) and the ten live instruments settle in
about 24 MB of JS heap — lighter than plotly.js loads before drawing its
first figure.
One self-contained directory under src/experiments/<subject>/<id>/,
scaffolded by npm run new:experiment:
manifest.js— params (field factories), views (view factories), validationscenes.js— the lecture script (auto-discovered)compute.js— pure, seeded, stateless science (runs in a worker)check.js— numerical correctness checks (npm run check)
The core discovers experiments automatically; adding one never modifies the
core. Reference example: src/experiments/estimation/confidence-intervals/.
See CONTRIBUTING.md — the experiment contract, the check requirement, and the licensing terms contributions are accepted under.
AGPL-3.0 — see LICENSE.