Guide · 01 of 15
Lessons & Slides
A Prism file is either a bare scene { ... } (one visualization) or a lesson { ... } (a full multi-slide lesson). A slide is a composition: any prose, any callouts, a scene (or two, as linked views), at most one exercise, and any number of goals. Prose, callouts and the scene show up in the order you write them, then the goals, then the exercise. There is no "text slide" vs "quiz slide"; you just include the parts you need.
lesson#
The top-level container. Holds lesson metadata and an ordered list of slides. These properties are the source of truth for how the lesson appears in the catalog: unit groups it under a heading on the course page, difficulty and icon render on its card, and summary is the one-line description. course tags which track it belongs to; skills lists the skill ids it teaches. requires lists the hard prerequisite skills: a beat: check slide whose exercise tests one of them is skipped for a student who got that skill right recently, so nobody re-proves what they just showed. kind: "bank" makes a practice bank instead of a lesson: many questions the student works through at their own pace across several sittings. Every slide in a bank is a question with a skill:, since mastery is what a bank is for. Consecutive slides with the same cat: form a round (aim for about 10 questions), the student gets a stop point after each round, and can jump to any question from a grid. A bank has no detours, so a wrong answer shows the ! worked solution, which should end with a [link](lesson:key) back to the lesson that teaches it. Banks are optional on the course page and never block the next lesson, and a bank counts as finished once every question has an answer. An unknown property here is a compile error, so a typo cannot silently drop your metadata.
lesson "Title" { [course: "..."] [unit: "..."] [difficulty: "Beginner"|"Intermediate"|"Advanced"] [icon: "..."] [summary: "..."] [skills: ["...", "..."]] [requires: ["..."]] [kind: "bank"] slide "..." { ... } ... }
| prop | type | description |
|---|---|---|
| course | string | track id, e.g. "calculus" |
| unit | string | unit heading on the course page |
| difficulty | "Beginner" | "Intermediate" | "Advanced" | shown on the lesson card |
| icon | string | lesson card icon, one of: Atom, Check, ChevronRight, Circle, Dumbbell, FlaskConical, FunctionSquare, LineChart, Link, Lock, Magnet, MapPin, Move, Play, Ruler, Sigma, Star, TestTube, TrendingUp, Triangle, Undo2, Waves, X, Zap |
| summary | string | one line describing the lesson |
| skills | string[] | skill ids this lesson teaches |
| requires | string[] | hard prerequisite skills; recently mastered ones skip their check slides |
| kind | "bank" | a practice bank: every slide a skill-tagged question, rounds by cat:, no detours |
Example
slide#
One screen of a lesson. Compose it from prose, callouts, an optional scene, an optional exercise (quiz/build), and optional goals. Prose and callouts written after the scene show below it. cat groups slides into sections; skill tags the specific skill this slide drills. beat says which part of the lesson standard the slide is (hook, explore, trap, name, transfer...), so dsl verify --standard can warn about a lesson with no trap, no transfer, or symbols named before anything was explored.
slide "Title" { [id: "..."] [beat: explore] [cat: "..."] [skill: "..."] > prose... definition|trap|tip|note|more ["..."] { ... } scene ... { ... } <exercise> goal "..." { ... } }
| prop | type | description |
|---|---|---|
| cat | string | section/category label |
| id | string | stable id. Required on any slide with an exercise, since the answer, the attempts and the review card are stored under it. Otherwise auto-slugged from the title |
| skill | string | skill id this slide targets |
| hidden | boolean | keep off the main path; only reachable as an onwrong: detour |
| then | string | on a hidden slide: the next hidden slide of the same detour (up to 3 long) |
| beat | check | bridge | hook | explore | reveal | trap | name | breaker | example | transfer | detour | the beat this slide plays in the lesson, for the linter |
Example
>#
A prose line. Everything after > is raw text — markdown (**bold**, *italic*) and $inline$ / $$display$$ LaTeX all work. Consecutive prose lines join into one paragraph, and a bare > line starts the next. Lines starting - make a bullet list and 1. a numbered list (no nesting; indent a line two spaces to continue the item above it). Lines that start and end with | make a table: a delimiter line like |---|--:| as the second line makes the first a header and sets alignment (:-- start, :-: centre, --: end), and a literal pipe in a cell is \| (pipes inside $...$ are safe). Lists and tables work in prose and callouts, not in quiz options or questions. [words](lesson:key) links to another lesson, the way to point a student at a missing prerequisite.
> markdown text with $latex$Example
The discriminant tells you how many roots exist:
- : two roots
- : one root
| roots | |
|---|---|
definition#
A boxed definition, for wording the student should be able to quote back. It is a slide item like prose, so it sits wherever you write it: above the scene, below it, or between two paragraphs. The title is optional (the box says "Definition" without one) and source: says where the wording comes from, like a textbook page. Inside go > lines, the same as prose: markdown, $math$, lists, tables, colour roles and ${answer(...)} all work. trap, tip, note and more are the same shape with a different job.
definition ["Title"] { [source: "..."] > text... }
| prop | type | description |
|---|---|---|
| source | string | where the wording comes from, shown small under the text |
Example
trap#
A warning box for the mistake most students make at this exact point. Same shape as definition. Put it where the wrong idea would form, not in a list at the end of the lesson.
trap ["Title"] { > text... }
Example
tip#
A shortcut or a way to remember something. Same shape as definition.
tip ["Title"] { > text... }
Example
note#
A quiet aside: a convention, a unit, something worth knowing that is not the main point. Same shape as definition.
note ["Title"] { > text... }
Example
more#
A folded box the student opens when the main explanation did not land. It stays closed until tapped, and without a title it asks "Didn't get it?". Put a second angle on the idea inside (an analogy, a smaller case), not the same words again. Same shape as definition.
more ["Title"] { > text... }
Example
law#
A named formula, set on its own line where you write it and collected into the formula sheet of the lesson and of its course. tex: is the formula itself with no $ around it; colour the quantities with \textcolor{current}{I} so they match the prose and the scene. units: is one short line under it saying what each symbol is measured in, and > lines add a sentence when the formula needs one. Use it once per formula, on the slide where the student has just worked it out, never as an opener.
law "Name" { tex: "..." [units: "..."] [> text...] }
| prop | type | description |
|---|---|---|
| tex | string | the formula, in TeX, without $ signs |
| units | string | the units of each symbol, as text with $math$ |
Example
worked#
A worked solution, one line at a time, each with the reason it follows. A step is the math itself in TeX with no $ around it. Lines line up on their first = (or <, \le, \Rightarrow...), and a step that starts with = carries on the line above. why: is the reason in plain words, beside the line on a wide screen and under it on a narrow one. Wrap what just changed in \new{...} to colour it, so the eye goes straight there. detail { step ... } folds finer steps under a step for whoever needs them, one level deep. The student taps through the steps, and > lines after the last step show once every step is out. Use it for the worked example of the example beat, then let them do the next one.
worked ["Title"] { step "..." [{ why: "...", detail { step "..." } }] step "..." [> text...] }
| prop | type | description |
|---|---|---|
| why | string | why this step follows from the one above |
| detail | block | finer steps folded under this one |
Example
- Chain rule: the power comes down, the inside stays, times the inside's derivative
term#
A glossary entry, declared at lesson level beside role. It shows nowhere by itself: mark the word in a slide's text as [word]{term:key} and the student can tap it to read the meaning, and every term of a course is listed on its glossary page. The key is lowercase latin (a-z, digits, _) even in an Arabic lesson; the word and its meaning are in the lesson's language. Keep a meaning to a sentence or two in plain words, it cannot point at another term. Mark the word the first time it matters on a slide, not every time. A term can be tapped in prose, callouts, laws and the question of an exercise (ask), never in its options, hints or steps and never in a goal.
term key "Word" { > what it means... } > ... [the word]{term:key} ...
Example
goal#
Gates the slide's Continue until the learner satisfies when (a boolean expression over the scene's state, which latches once true). Use it for guided tasks like "drag the vertex below the axis". Hints stay hidden until the learner has been stuck for a while, then arrive one at a time, broadest first. Once the last hint is out, showme offers a Show me button that animates the scene to the given values, so the goal gets met in front of them and is marked as helped. dsl verify checks that the goal starts unmet and that showme really meets it. Put after: goals inside the slide's exercise to keep the question hidden until every goal is met, so the student explores first and gets asked second.
goal "Instruction" { when: <condition>, [hints: ["...", "..."]], [showme: {k: v}], [dur: <ms>] }
| prop | type | description |
|---|---|---|
| when* | expr | boolean condition over scene state |
| hint | string | a single nudge shown if the learner is stuck |
| hints | [string] | a ladder of nudges, broadest first, one more per tap |
| showme | { key: value } | state the Show me button animates to, which must meet the goal; a choice takes one of its options in quotes |
| dur | ms | how long the Show me animation takes (default 2000) |
Example
role#
Colour roles tie a word in the text to the thing it names in the picture. Declare role distance = accent once at the top of the lesson, then use distance wherever a colour goes: color: distance on a scene object, [the distance]{distance} around words in prose, questions and goals, and \textcolor{distance}{d} inside math. Everything playing that role comes out the same colour, so "distance" in the sentence and the distance leg on the graph are visibly the same thing. The colour is one of primary, accent, success, danger, warning or neutral, and about three roles per lesson is plenty. Physics has five roles built in that need no declaration: current, voltage, resistance, charge and field (the magnetic field: its lines and the symbol B). Each is a colour and a role at once, so [the current]{current}, color: current and \textcolor{current}{I} work in any lesson and a quantity looks the same everywhere.
role <name> = <colour> [words]{name} $\textcolor{name}{x}$ color: name
Example
answer#
Lesson memory. Inside prose, a question or a goal, ${answer("slide-id")} shows what the student answered on an earlier quiz or numeric slide (the option they picked, or the number they typed), and ${recall("name")} shows the last value of a keep param. This is how a reveal can say "you guessed rounding" and how the naming slide can use the number the student found themselves. The slide has to come earlier in the lesson, which the compiler checks. Before there is an answer the fallback shows, or ? if you gave none.
${answer("slide-id")} ${answer("slide-id", "fallback")} ${recall("param")}
Example
live#
Explorable text. In prose, a callout or a law, ${expression} shows a value read off the slide's scene and follows it as the student moves things: any state (param, bool, choice) and, beside a circuit, its readings (I_X, V_X, P_X, Req). It is rounded like a label, so reach for fixed(x, 2), sig(x, 3) or sci(x, 2) to say how it is written, and it works inside $...$ and **bold**. [text]{drag:name} turns the text into a number the student drags sideways (or moves with the arrow keys) to change the param name, which needs a range:; put ${name} in the text so it shows what it sets. Use them for the one sentence that states the relation, so the sentence itself stays true while the diagram moves. The slide needs a scene, and neither works inside an exercise or a goal.
> ... ${expression} ... > ... [${R} Ω]{drag:R} ...
Example
V_UNBALANCED#
A written chemical equation is checked for balance. dsl verify --standard reads every $...$ that holds a reaction arrow (\rightarrow, \to, \xrightarrow, \rightleftharpoons) and warns V_UNBALANCED with what differs (Fe: 2 left, 3 right; charge: 0 left, +1 right). An equation it cannot read is left alone: word equations, \text{} species, unknown letters like X, and one species turning into another (Mn^{2+} \to Mn^{3+}). A quiz is checked in its correct option only, so a skeleton shown as a wrong option can stay unbalanced. Put \relax inside an equation to opt it out.
$\mathrm{2Fe + 3Cl_2 \rightarrow 2FeCl_3}$
onwrong#
Adaptive branching. Goes inside any exercise: if the learner gets it wrong, they take a detour to the named slide (which must be a hidden: true slide in the same lesson) and then come back. A scaffold is an ordinary slide, so it can carry its own prose, scene, and exercise. Add retry to return the learner to the original question for another attempt; leave it off to move them forward instead.
Different mistakes usually mean different misconceptions, so each can get its own detour: put onwrong: on a quiz option, or add a wrong <value> -> "slide-id" line to a numeric or a table blank (with an optional "why" for the mistake). The exercise-level onwrong: catches every wrong answer nobody named. Each detour fires at most once per question, so a student who comes back and makes a different mistake still gets the fix for that one, and detours cannot chain. A detour that needs more than one slide (a short bridge for a missing prerequisite) links its hidden slides with then: "next-id", up to three in a row, before sending the learner back.
onwrong: "slide-id" [retry] - "wrong option" { why: "...", onwrong: "slide-id", retry } wrong <value> ["why"] [-> "slide-id" [retry]] miss "left" -> "right" "why" [-> "slide-id" [retry]] blank(answer) { wrong <value> ["why"] [-> "slide-id" [retry]] }
| prop | type | description |
|---|---|---|
| onwrong* | string | id of the hidden slide to detour to |
| retry | flag | send them back to the question afterwards instead of onward |
Example