Interactive explainers
This page covers the interactive explainer system: canvas-based visual walkthroughs that open in a separate browser window so the student can study concepts alongside their code editor.
School Platform series
- School Platform
- Architecture
- AI features
- Calendar integration
- Programme
- Explainers - You are here
- Schedule
- Integrations
- Deployment
What explainers are
Explainers are multi-screen interactive visualisations that teach a specific programming or maths concept. Each one opens in a separate browser window via window.open() so the student can position it next to their IDE without overlaying the tracker page. If the popup is blocked, it falls back to an inline expandable panel.
How it works
-
StepExplainer receives a
taskCodeand looks up the registry. If an explainer exists, it renders a clickable card on the task row. -
ExplainerShell is the generic canvas and navigation component. It handles responsive scaling (640x400 internal resolution), Back/Next buttons, progress text, pointer events, an animation loop for screens that set
animate: true, and a pause/play toggle. -
Registry maps task codes to
ExplainerConfigobjects. Adding a new explainer requires one file and one registry entry. -
Explainers tab on each phase page collects all registered walkthroughs for that phase in a card grid. Each card shows its location in the programme (e.g. "Module 2, Part 2, Step 2") parsed from the task code.
Key interfaces
interface ExplainerScreen {
title: string
explain: React.ReactNode
draw: (ctx: CanvasRenderingContext2D, w: number, h: number, state: Record<string, number>) => void
onPointerMove?: (x: number, y: number, state: Record<string, number>) => void
animate?: boolean
initState?: () => Record<string, number>
}
interface ExplainerConfig {
cardTitle: string
cardDescription: string
screens: ExplainerScreen[]
}
Adding a new explainer
- Create a file under
src/app/programme/_components/explainers/(e.g.my-topic.tsx) that exports anExplainerConfig. - Import it in
explainers/registry.tsand add a key matching the task'scodefield. - Done.
StepExplainerautomatically renders the card for that step, and the Explainers tab lists it alongside other walkthroughs for the same phase.
Design decisions
| Decision | Rationale |
|---|---|
| Fixed 640x400 internal resolution | Consistent aspect ratio; CSS scales to fill container; pointer coordinates scaled accordingly |
Colour palette: #378ADD, #EF9F27, #1D9E75, #D85A30, #7F77DD | Matches the tracker's dark theme |
| Animation throttled to ~30 fps | Values are readable; achieved by incrementing state.frame every 2nd requestAnimationFrame call |
| Pause/play toggle | Lets the student freeze the canvas to study a particular frame |
| Self-contained files | No shared mutable state between explainers; each file is independent |
isDark() helper for SSR safety | Guards window.matchMedia calls that would fail during server rendering |
| Popup window route is anonymous | Read-only and kid-facing; no auth gate needed |
File layout
src/app/programme/_components/
├── explainer-shell.tsx # ExplainerShell component
├── step-explainer.tsx # StepExplainer entry card (inline in step)
├── explainer-grid.tsx # ExplainerGrid card grid (Explainers tab)
└── explainers/
├── registry.ts # Registry + getExplainersForPhase helper
├── distance-formula.tsx # Collision detection explainer
├── frame-counter.tsx # Frame % cycle mechanics explainer
└── sine-walking.tsx # Sin()-based walking explainer
src/app/programme/explainer/
└── [code]/page.tsx # Popup window page