psi-slides · a figure language for lectures

Figures you write

You already have a way to make figures. PowerPoint, Keynote, Illustrator, Inkscape, TikZ, Mermaid, … – and for a picture that is drawn once and never revised, those are the right tools.

We believe lecture figures are different. They should be built up in front of a room while you talk over them, survive the slides being reordered, and come out of the same file as the printed handout. Ideally a diff shows what changed, a comment can sit in the source without reaching the screen, arrows move with the boxes they join, and a language model can read a figure and edit it.

In psi-slides, a figure is a few lines of text in the lecture’s own Markdown file, and the build – the one command that turns that file into slides – draws it.

Development status: the ::: draw mechanism is in both ways of getting psi-slides, but not yet in the format promise: it came after 1.0.0, so a figure you write today may need an edit when the language reaches a release. The graphical editor is experimental and desktop-oriented: it has not yet been tested broadly.

TCP IP Ethernet one frame payload the next hop

Open it as a lecture

Animated Infographics is a complete psi-slides lecture. Its figures live inside its Markdown source, the build places them into the slides, and the same source becomes the projection, the speaker cockpit and both reading views.

Start with the projection: Space steps a figure forward. S opens a speaker cockpit that follows the projection.

Figures in psi-slides

A figure is whatever a slide shows in place of plain text – such as an image or a video clip. Our focus is on relational drawings, written inside a ::: draw block. They are described in text, drawn when you build the slides, and can be built up stage by stage while you present.

Seven lines, and that is the whole figure, exactly as it goes into a slide. Nothing in it is placed by coordinates: Eve is placed against Alice, Bob against Eve. An arrow is written by naming its two ends, and it finds its own way between them.

plaintext plaintext Alice Eve Bob
::: drawbox alice "Alice"box eve "Eve"  right of alice gap 1.7 {.accent}box bob "Bob"  right of eve gap 1.7edge alice -> eve "plaintext"edge eve -> bob "plaintext":::

A figure can arrive one beat at a time

The same three boxes, with two beats added – keyframes, in the sense video editing uses the word. Step through it: the second beat renames Eve, and her box, both arrows and the outline around her move without a line of source mentioning any of them.

Eve's reach message Alice Bob EveEve, on path
::: draw 170x56box  alice  "Alice"box  bob  "Bob"   right of alice gap 10.3box  eve  "Eve"   between alice,bob offset 0,1.5 {.accent}edge direct alice -> bob "message"edge alice -> eve {.accent}edge eve -> bob {.accent}container zone "Eve's reach" over eve pad 0.45 {.dashed .muted}step intercept  show eve  hide directstep relabel  label eve "Eve, on path"  emph eve:::

A real lecture figure, built in four beats

A passkey registration: four participants, nine messages, two notes. The numbering, the spacing and the lines down the page are generated. The four beats build it the way it is read to a room, one exchange then the next, and one of the two notes is held back a beat so it lands after the exchange it comments on.

on the device, over CTAP clicks Create passkey request registration options registration options CTAP authenticatorMakeCredential prompt: PIN or biometric user verified locally attestation object attestationObject + clientDataJSON verify signature · check origin User Browser Authenticator Relying Party 1 2 3 challenge · rp.id · user.id · algs 4 clientDataHash · rp.id · user · algs CTAP runs over USB, NFC or BLE 5 6 generate key pairbind to SHA-256(rp.id)store privately · emit publicly 7 authData (public key, cred ID) · signature 8 clientDataJSON carries challenge · origin 9 the challenge is what makes it fresh
::: draw 150x40# The actors are lines of their own because each needs a name later lines can# hold on to and an attribute tail of its own. Everything under them is a# message - an arrow between two names - or a note.sequence wa at 0,0  actor u  "User"  actor br "Browser"  actor au "Authenticator" {.tone-3}  actor rp "Relying Party"  u  -> br "clicks Create passkey"  br -> rp "request registration options"  br <- rp "registration options" "challenge · rp.id · user.id · algs" {.dashed}  br -> au "CTAP authenticatorMakeCredential" "clientDataHash · rp.id · user · algs"  note br,au "CTAP runs over USB, NFC or BLE"  au -> u  "prompt: PIN or biometric"  u  -> au "user verified locally"  note au "generate key pair\nbind to SHA-256(rp.id)\nstore privately · emit publicly"  au -> br "attestation object" "authData (public key, cred ID) · signature" {.dashed}  br -> rp "attestationObject + clientDataJSON" "clientDataJSON carries challenge · origin"  rp -> rp "verify signature · check origin"# Two annotations the statement knows nothing about, hung off generated# names: a brace over three messages and a note beside one of them. A beat# can show or emphasise either one exactly as it would a box.brace ctap over wa-3,wa-4,wa-5 pad 0.3 "on the device, over CTAP" side left {.small .turn}text fresh "the challenge is what makes it fresh" right of wa-2 gap 1.9 {.small .hand} -- wa-2# The beats build the protocol rather than only lighting parts of a finished# one, which is how it is read in front of a room: one exchange, then the next.# Two things arrive on their own and neither is named where it appears. The# brace is never shown by a step - it holds wa-3, wa-4 and wa-5, and a brace is# only as visible as its members, so it comes in with them. The annotation is# the opposite case: it hangs off wa-2 by a leader and would arrive with it, so# an explicit `show` one beat later is what holds it back until the exchange# it comments on has been read.step ask-for-options  show @wa-msg-1, @wa-msg-2  emph @wa-msg-2step the-challenge-is-the-point  show fresh  dim @wa-msg-1, @wa-msg-2step on-the-device  show @wa-msg-3, @wa-msg-4, @wa-msg-5  show wa-note-0, wa-note-1  emph austep back-to-the-relying-party  show @wa-msg-6, @wa-msg-7, @wa-msg-8  dim au  emph @wa-msg-8:::

Four design principles

The text is the figure

There is no second copy of it: no drawing file beside the lecture, no exported image one edit behind its source. The figure is in the same file as the prose around it, so it travels when a slide is moved or copied, and a change to it arrives in a diff as the lines that changed.

You can still drag it. The live slides carry a graphical editor, and a drag keeps the relation and changes the number inside it: right of alice gap 1.1 becomes right of alice gap 1.6. Open the slides, bring up a ::: draw figure, click it and press E.

Positions are relations, and stay relations

right of alice gap 1.7 is the whole of what the file says about where Eve is, permanently – not shorthand the build resolves once and forgets. Almost every element is placed against another, so the placements form a set of dependencies that the build resolves in whatever order works.

Give Alice a longer label and her box grows; Eve stays 1.7 units to its right, Bob follows Eve, and both arrows find their new ends, with nothing moved by hand. If two relations point back to each other the build stops and names the placement cycle.

Each element once, then what the beats change

Every box, arrow, label and outline the figure will ever hold is written at the top of the ::: draw block, visible from the start or not. A static figure stops there. If it has beats, a step section follows for each, naming it and listing what it changes: visibility (show, hide), position (move), labelling (label), prominence (emph, dim, ghost) or styling (style).

An element starts hidden exactly when the first thing any step says about it is show, so nothing has to list what starts hidden.

A beat is the whole figure laid out again

Every beat is the same layout evaluated again with different inputs, not a movement played over a finished picture, and the build works every beat out before the page is written. The browser is handed one drawing and a table of numbers to slide between, with no layout engine to load.

So an arrow stays attached to a box that walks away. The arrow never held a coordinate; it held “the right-hand edge of eve”, and that still means the right-hand edge of eve wherever eve now is.

Dragging a box rewrites the line that placed it

A drag changes the number inside a relation and leaves the relation itself alone, so the figure can still be edited in its text afterwards without losing what holds it together. That makes this editor work differently from a drawing program: it shows you the relations, and it asks you to name your elements.

Two places to look in the window below: the words drawn on the canvas beside the box that is selected, and the panel down the right-hand edge.

the editor, opened on a figure with beats
The diagram editor open over a lecture slide: a dark canvas holding a CBC decryption figure with one box selected, the relations that place it written on the canvas beside it, a panel on the right describing the beat that is standing, a rail of the figure's beats along the bottom, and a strip of the lecture's other figures under that.
The editor, over a slide of a network-security lecture. The selected box is c1; the panel on the right describes the beat the figure is showing.

On the canvas, the words drawn beside the selected box c1gap 1.15, flush middle, align y middle – are the relations it was written with. A finished drawing cannot show those: a box placed gap 1.15 from its neighbour looks exactly like one that happens to sit 1.15 away, and only one of the two moves when the neighbour does.

In the panel down the right-hand edge, the beat now showing is described by what it does, not by the source that says it. It is written show @out and dim feed0, feed1, feed2; the panel names three boxes appearing, three arrows arriving with them – an arrow is only as visible as the two things it joins – and three lines dimmed underneath. A drag there writes a move into that beat rather than moving the element.

Click a figure in the projection and press E, or use the button in the corner of the card that click opens. The editor ships with any lecture that has a figure in it; editor: speaker at the top of the lecture file keeps it off the projection, and editor: none leaves it out altogether.

The gestures, the panel and the rest of it are in the manual. Automated tests cover the editor, but few people have used it, and it is made for a desktop-sized authoring screen.

The edit goes straight back into source.md if you build with --watch, the mode that rebuilds on every save. The button in the top right then reads Write to source.md, the build writes the block back into the file, and the rebuild that follows reloads every open tab. Without a watching build the same button copies the whole block to the clipboard, to paste over the old one. An edit that stops the block compiling is never written: it is rolled back and the compiler’s error message is shown.

What you give up, and what you get

A drawing programPowerPoint, Keynote, Excalidraw An auto-layout languageMermaid, Graphviz, PlantUML psi-slides::: draw
Where the figure lives A file of its own, beside the lecture Text, in the lecture or beside it Text, in the lecture file, between the sentences it belongs to
Who decides where things go You, in pixels The layout engine You, in relations between one element and another
Give one box a longer label The box grows; every arrow, outline and note around it is moved by hand The graph is laid out again, and can come back in a different arrangement Whatever was attached to it follows, and nothing else moves
Building it up in front of a room A timeline of appearances and moves, played over the finished picture There is no equivalent; a diagram is one static picture Beats, each of them the whole figure laid out again
The handout A second export, and a second thing to keep current The same picture The last beat of the same figure. Emphasis a beat lent an element is gone; emphasis written into the figure itself stays
Output and export Standalone image, SVG or PDF, depending on the program Usually SVG, PDF or PNG Inline SVG in the lecture HTML. There is no standalone SVG or figure-level PDF export yet; the complete lecture or handout can be printed to PDF
When to reach for it One figure, drawn once and not revised. Nothing to install, no build step, and most people already have it Forty nodes whose arrangement carries no meaning. It places everything for you, which ::: draw will never do When the arrangement is part of the argument, or the figure has to be built up in front of a room

This page argues for the language. Figures You Write is the manual: it builds a figure a line at a time, gives it beats, then lists every class and statement, fifteen design rules and a set of worked examples.