Choreography is where courses go from “slides with voiceover” to “narrated film.” The screen should never be static while narration is playing.
The Density Rule
Every sentence must have at least one trigger. Many sentences need two or three.
A paragraph with no triggers is a bug. Fix it by adding a focus shift, a zoom change, an annotation, or a show/hide.
Why Density Matters
Triggers fire at their word position. TTS speaks the narration. The listener’s eyes track what’s changing on screen. If nothing changes, the listener’s attention drifts. If the visual changes align perfectly with the spoken words, the listener is locked in.
This is Mayer’s temporal contiguity principle taken to the extreme: not just “narration and visuals at the same time” but “narration and visuals synchronized at the word level.”
Minimum Trigger Density
| Paragraph type | Minimum triggers | Typical triggers |
|---|
| Introducing a block | 1 (show) | 1-2 (show + focus) |
| Walking through code | 2 (focus + annotate) | 2-3 (focus + zoom + annotate) |
| Comparing two things | 2 (focus left + focus right) | 3-4 |
| Making a point | 1 (zoom or annotate) | 1-2 |
| Transitioning | 1 (clear or unsplit) | 1-2 (clear + show) |
| Concluding a section | 1 (zoom reset or focus none) | 1-2 |
Animation Selection
Which Animation for What
| Situation | Effect | Duration | Easing | Example |
|---|
| First code reveal | typewriter | 1.5-2s | linear | {{show: hash-fn typewriter 2s linear}} |
| New concept replaces old | slide | 0.3-0.5s | ease-out | {{show: solution slide 0.3s}} |
| Result or conclusion | slide-up | 0.3s | ease-out | {{show: result slide-up 0.3s}} |
| Focal element appearing | grow | 0.4-0.5s | spring | {{show: diagram grow 0.5s spring}} |
| Data structure snapping in | grow | 0.3-0.5s | spring | {{show: tree grow 0.4s spring}} |
| Preview alongside code | slide | 0.5s | spring | {{show: preview slide 0.5s spring}} |
| Reference material | none | — | — | {{show: table none}} |
| Hiding something | fade | 0.3s | ease-out | {{hide: old-code fade 0.3s}} |
| Scene break | slide | — | — | {{clear: slide}} |
Duration Guidelines
- Micro-interactions (focus shifts, annotations): instant state changes, no duration
- Content transitions (show/hide): 0.3-0.5s—fast enough to not feel sluggish, slow enough to register
- Typewriter reveals: 1.5-2s for 10-20 lines. Aim for ~100ms per line
- Scene clears: use the default
Never exceed 400ms for product UI interactions. Only typewriter (a deliberate slow reveal) goes longer.
Typewriter Choreography
Typewriter makes code appear line by line, creating a “live coding” feel.
When to Use
- The FIRST time a code block appears in a step
- When you want the audience to absorb code incrementally
- When the code is the star of the scene (not a supporting element)
When NOT to Use
- Re-showing a block that was visible earlier (use
slide or fade)
- Supporting code that accompanies a primary visualization
- Blocks over 40 lines (pacing breaks down)
- Quick reference or boilerplate code
duration = lines * 0.1s is a reasonable starting point:
- 10 lines → 1s
- 15 lines → 1.5s
- 20 lines → 2s
Don’t go faster than 50ms/line (too glitchy) or slower than 150ms/line (too tedious).
Narration During Typewriter
Start narrating while lines are still appearing—the audience reads ahead naturally. Don’t wait for the typewriter to finish before speaking.
{{show: dijkstra typewriter 2s linear}} Here's Dijkstra's algorithm in code.
{{focus: init}} {{zoom: 1.3x}} Set every distance to infinity except the start.
Zoom Choreography
Zoom is the most underused and most impactful tool. It controls what the learner can actually read.
The 15-Line Rule
Any code block over 15 lines MUST have zoom choreography. At normal scale (1x), the bottom of a 20+ line block is below the viewport. The learner sees a focus highlight but can’t read the code. This is a failure.
The Zoom Pattern
1x Show full block for context
↓ focus
1.2x-1.3x Zoom in on focused region
↓ explain
1x Reset before jumping to a distant region
↓ focus
1.2x-1.3x Zoom in on new region
↓ explain
1x Reset to show full picture
Zoom Scales and Their Purpose
| Scale | Purpose | When |
|---|
0.8x | Zoom out to show more context | Rarely. Only for very wide diagrams. |
1x | Normal. Full block visible. | Reset between focus jumps. End of section. |
1.1x | Slight emphasis. Current context. | Gentle nudge: “look at this area.” |
1.2x | Standard zoom. Key insight. | Most common zoom level for code walkthrough. |
1.3x | Dramatic. Critical moment. | Factorial growth, key formula, the aha moment. |
1.5x+ | Extreme. Single line emphasis. | Rare. Only for very important single lines. |
Zoom + Focus + Annotate (The Triple)
The most powerful choreography pattern combines all three:
{{focus: pick}} Now the key step. {{zoom: 1.2x}} Pick the unvisited node with the smallest known distance. {{annotate: pick "Greedy choice"}}
- Focus fires → region highlighted, everything else dimmed
- “Now the key step” → builds anticipation
- Zoom fires → region enlarged, legible
- Explanation follows while zoomed in
- Annotation fires → concept labeled
This triple is the workhorse of code walkthroughs.
Zoom Transitions
Always reset to 1x before jumping to a distant region:
{{zoom: 1x}} {{focus: return-val}} {{zoom: 1.2x}} Down at the bottom, the return value.
If you jump from 1.3x on line 3 to 1.3x on line 18, the viewport snaps violently. Resetting to 1x first shows the full block, then zooms into the new target smoothly.
Focus Choreography
Focus is your primary attention tool. Default to focused.
Always Focus After Showing
An unfocused 20-line code block is overwhelming. The listener doesn’t know where to look.
{{show: hash-fn typewriter 2s linear}} Here's our hash function.
{{focus: signature}} It takes a string and returns a number.
Show → establish context → focus immediately on the first thing you’ll explain.
Walk Regions Top to Bottom
Read code in reading order. Focus shifts should follow the natural flow:
{{focus: signature}} The function signature.
{{focus: init}} Initialize the accumulator.
{{focus: loop}} The loop processes each character.
{{focus: return-val}} Return the result.
Jumping from line 15 to line 2 to line 20 is disorienting. If you must jump, reset focus and zoom first.
focus: none
Use none to clear focus in two situations:
- Show the whole picture: “Step back and look at the whole function.”
- Before clearing: Reset before
{{clear}} so the transition feels clean.
{{focus: none}} {{zoom: 1x}} That's the whole algorithm. Every step justified.
{{clear: slide}}
Split Choreography
Split mode shows two blocks side by side.
Entry Pattern
{{split}} {{show: source slide 0.3s}} {{show: preview slide 0.5s spring}} Here's the code and what it produces.
split enables multi-panel mode
- Left panel enters first (0.3s)
- Right panel enters slightly delayed (0.5s) with a playful spring
- Narration begins after both are visible
Convention: Left = Code, Right = Visual
The explanation/source is on the left. The result/preview/diagram is on the right. This convention means the learner always knows where to look.
Working in Split Mode
While split, you can focus on either panel’s regions:
{{focus: signature}} On the left, the function signature.
{{focus: button-text}} On the right, see how it renders.
Focus works across panels—the system finds which block contains the region.
Exit Patterns
Unsplit (keep one):
{{unsplit}} Now just the code.
Keeps the last-shown block. The other panel exits.
Clear (fresh start):
{{focus: none}} {{unsplit}} {{clear: slide}}
Clean exit: unfocus, unsplit, clear. Complete reset.
Never use more than two panels. Three-panel layouts are too cramped and confuse the eye.
Clear Choreography
Clear is a scene break. Use it between conceptual sections.
Transition Types
| Transition | Feel | Use when |
|---|
slide | Directional, progressive | Moving to the next concept (default) |
fade | Gentle, connected | Bridging related ideas |
instant | Hard reset, no ceremony | Starting a completely new section |
Clear Frequency
2-3 clears per step is typical. More than 4 means your step has too many concepts—split it.
What Clear Resets
Everything. After clear:
- Slots: empty
- Focus: none
- Annotations: gone
- Zoom: 1x
You must {{show: ...}} something new after every clear. A clear followed by narration with no show means the audience hears narration while looking at nothing.
Pre-Clear Cleanup
Before clearing, clean up the scene:
{{zoom: 1x}} {{focus: none}} That's the complete picture.
{{clear: slide}}
{{show: next-thing}} Now let's look at...
This feels intentional. Resetting zoom and focus → brief pause with full view → clean transition → new content.
Transform morphs one data block into another. Nodes with the same ID slide smoothly from old to new positions.
Every algorithmic operation follows a three-beat rhythm:
- Setup — focus the region about to change
- Trigger —
{{transform: v0->v1}} fires the structural change
- Resolve — narrate what just happened while the animation plays
Tree Operations
Insert into BST:
{{focus: leaf-parent}} We compare with six. {{transform: tree-v0->tree-v1}} Four goes left. The new node slides in from its parent's position.
Rotation (AVL/Red-Black):
{{annotate: unbalanced "bf=2"}} This node is unbalanced.
{{transform: avl-v0->avl-v1}} A right rotation fixes it. Watch the nodes slide to their new positions.
Rotations are the most visually dramatic transform—three nodes rearrange simultaneously. Keep narration short so the viewer watches the movement.
Hash Map Operations
Insert with hash computation:
{{annotate: input "h(key)=3"}} Hash of the key lands in bucket three.
{{focus: bucket-3}} That's this bucket.
{{transform: hm-v0->hm-v1}} The new entry appends to the chain.
| Operation | Recommended pause | Why |
|---|
| Single node insert/delete | 0s (narrate immediately) | Simple, viewer grasps instantly |
| Rotation | 1-2s pause | Three nodes moving simultaneously |
| Split (B-tree) | 1-2s pause | Complex structural change |
| Consolidation step | 0.5-1s | Repetitive, keep pace up |
| Path compression | 1s | Multiple edges changing at once |
| Recoloring | 0s | Subtle, narrate over it |
The Storyboard Test
Before writing narration, sketch the sequence of screens your step will produce.
Each screen = one scene. A scene is: which blocks are visible + which region is focused + any annotations + zoom level.
Scene 0: [empty]
Scene 1: [hash-fn] — typewriter reveal
Scene 2: [hash-fn, focus: signature, zoom: 1.3x, annotate: "The API"]
Scene 3: [hash-fn, focus: loop, zoom: 1.2x]
Scene 4: [hash-fn, focus: return-val]
Scene 5: [hash-fn, focus: none, zoom: 1x]
Scene 6: [clear: slide]
Scene 7: [buckets] — enter from right
...
If the storyboard feels choppy, rethink the sequence. If there are big jumps between scenes, add intermediate steps. If three scenes in a row look the same, you’re not using focus/zoom/annotate enough.