The problem
Most algorithm visualisers are animations. You press play, bars swap or nodes light up, and you nod along. It feels like learning and it mostly isn't: watching a sort finish teaches almost nothing about why the pivot went where it did, and it certainly doesn't prepare you for being asked to reason about it in an interview.
I mentor developers making the switch into tech, many of them without a computer science background, and I kept seeing the same gap. They had watched plenty of animations and could still not say what a queue would contain three steps into a breadth-first search. The cheapest intervention the learning research points to is also the simplest: ask the learner to predict before you reveal. Prism is built around that one idea.
What it does
Prism has 42 algorithm visualisations spanning sorting, searching, graphs, trees, dynamic programming, recursion and backtracking, linked lists, strings and greedy algorithms, plus separate Systems, Concurrency and Theory sections. Every algorithm runs as a real trace of the real implementation, and the trace can be scrubbed and rewound to any step with the source line highlighted. At genuine decision points, playback pauses and asks the learner what happens next: which element gets compared, which half of the array survives, what gets dequeued. A wrong answer gets an explanation of why the intuition fails, not just the right answer. There is a compare view that runs two algorithms on the same input against one clock, search across algorithms and essays, light and dark themes, and per-algorithm social cards. Progress is stored only on the learner's device.
Key engineering decisions
Algorithms are generators; the trace is an event log
Each algorithm is written as a TypeScript generator that yields typed steps from a small vocabulary: compare, swap, enqueue, pushFrame, decision and so on, each tagged with the source line it corresponds to. A recorder drains the generator into a trace with an initial scene and a step list. That separation means the algorithm code reads like the textbook version, renderers never see algorithm code, and the same trace drives the 2D, 3D and text-only views. The trade-off is that adding a step kind means teaching every renderer about it, so smoke tests fail on any renderer that ignores a kind. That is deliberate friction.
Scrubbing uses snapshots and replay, not inverse steps
Rewinding is the hard part of a visualiser. Inverse steps (an "unswap" for every swap) are elegant and wrong roughly once per algorithm. Instead, the cursor keeps periodic snapshots of the scene, capped at 512 per trace, and materialises any step by cloning the nearest snapshot and replaying forward. Stepping forward mutates in place with no clone at all. This is the event-sourcing trade: the log is the truth and snapshots only bound replay cost. Long traces are generated in a Web Worker and streamed in chunks, so playback starts on the first chunk while the rest is still being computed. The cost is memory for snapshots and some replay latency on very long traces; the benefit is that rewind is always correct.
A prediction is a step, not a side feature
Decision points are not bolted on afterwards. The generator itself yields a decision step at the moment it makes a choice, carrying the prompt, the form of the answer (pick an element, choose an option, or give a number), the correct answer and, most importantly, why the wrong intuition fails. The algorithm knows the answer because it is the algorithm. The player only asks when the learner crosses a decision point moving forward; scrubbing back over one never re-asks, because predicting a future you have already seen isn't a prediction. Prediction is on by default and "just watch" is the opt-out. The trade-off is that every algorithm author has to write good questions, so a contract test enforces that decisions exist, are computable and are always explained.
Nothing re-renders per frame
The player mutates a frame object in place on a single requestAnimationFrame loop and React only hears about step boundaries and transport changes. The 3D array view uses instanced meshes with memoised geometry, and three.js is split into its own chunk that only loads on routes that need it. A Playwright performance test asserts frame-rate history stays within budget. The trade-off is a less idiomatic React codebase, in exchange for smooth playback on ordinary laptops.
Stack
Astro renders the essays and catalog as static HTML, so the explanatory text is in the served source for search engines. The player is a React island with a Zustand store. Rendering is SVG for 2D and three.js through react-three-fiber for 3D, with a flat equivalent for reduced motion and small screens. Trace generation runs in Web Workers over Comlink. Progress lives in IndexedDB via Dexie, with no accounts and no server. It is deployed as static assets on Cloudflare Workers.
What's next
More algorithms in the categories that are still thin, and more decision points in the ones that have only a few. The theory decks already use spaced repetition on-device; I want the algorithm progress page to schedule reviews the same way.
