How I Built AlgoLens — Interactive Algorithm Visualizer from Scratch
Why I Built This
I've been preparing for Staff Engineer interviews over the past few months. One thing became clear early on — reading about algorithms and actually understanding them are two very different things.
When I studied Quick Sort, I could follow the logic. But I couldn't see the partition happening, couldn't feel the recursion splitting the array, couldn't intuitively grasp why the pivot choice matters. Every tutorial showed me pseudocode. Nobody showed me the algorithm thinking.
So I built AlgoLens.
Try it live → algolens-cyan.vercel.app
What AlgoLens Is
AlgoLens is an interactive algorithm and system design visualizer — 25+ step-by-step animated labs covering:
- Sorting — Quick Sort, Merge Sort, Heap Sort, Radix Sort, and more
- Searching — Binary Search, Two Pointer, Sliding Window, Interpolation Search
- Graphs — BFS, DFS, Dijkstra's Algorithm
- Trees — BST operations, all traversals, Trie
- Dynamic Programming — Fibonacci (memo vs tabulation), LCS with 2D table fill
- System Design — LRU Cache, Consistent Hashing ring visualization
Every visualizer shows you:
- The array state at each step with colour-coded bar states
- The active pseudocode line synced to the current operation
- A plain-English explanation of what's happening and why
- Time/space complexity at a glance
The Architecture Decision That Defined Everything
The most important decision I made: no external animation libraries.
No Framer Motion. No Three.js. No D3. No GSAP.
Everything — every bar animation, every colour transition, every step interpolation — is vanilla CSS and React state.
Here's why this mattered:
Control. Animation libraries make common animations easy. But a sorting visualizer needs unusual animations — bars swapping positions mid-sort, pivot bars highlighted differently from compare bars, recursive sub-arrays shown as highlighted ranges. Every library would have gotten in the way more than it helped.
Performance. With 100+ bars animating simultaneously during Radix Sort, you need tight control over what gets repainted and when. Vanilla CSS transitions on individual bar heights are extremely efficient — the browser's compositor handles them off the main thread.
Learning. The point of this project was deep understanding. Writing the animations from scratch forced me to understand exactly what each algorithm is doing at every step.
The Core Abstraction — AnimationStep
The cleanest architectural decision was a universal AnimationStep type that every algorithm must produce:
interface AnimationStep {
values: number[]; // current array state
compareIndices: number[]; // bars being compared (blue)
swapIndices: number[]; // bars being swapped (red)
sortedIndices: number[]; // confirmed sorted (green)
pivotIndex: number | null; // pivot bar (yellow)
activeRange: [number, number] | null; // highlighted subarray
codeLine: number; // synced pseudocode line
title: string; // step title
explanation: string; // plain-English narration
}
🎬 See it live — rather than a static diagram, open AlgoLens and watch Quick Sort partition in real time.
This is the contract. Every algorithm exports one function:
function createQuickSortSteps(values: number[]): AnimationStep[];
The visualizer shell (LessonLab) knows nothing about Quick Sort or Merge Sort — it just consumes steps and drives playback. Adding a new algorithm is three files:
src/lib/algorithms/youralgo.ts— the step generator- Register it in
src/lib/lessons.ts - Create a one-line route page
This is the same separation of concerns principle I apply to production systems — decouple the data model from the presentation layer. Here, the algorithm logic is completely isolated from the UI logic.
How Quick Sort Generates Steps
Here's a simplified version of how Quick Sort produces its animation steps:
function createQuickSortSteps(values: number[]): AnimationStep[] {
const steps: AnimationStep[] = [];
const arr = [...values];
function partition(low: number, high: number): number {
const pivot = arr[high];
let i = low - 1;
for (let j = low; j < high; j++) {
// Record compare step
steps.push({
values: [...arr],
compareIndices: [j, high],
swapIndices: [],
sortedIndices: [],
pivotIndex: high,
activeRange: [low, high],
codeLine: 4,
title: "Comparing elements",
explanation: `Comparing arr[${j}]=${arr[j]} with pivot=${pivot}`,
});
if (arr[j] <= pivot) {
i++;
[arr[i], arr[j]] = [arr[j], arr[i]];
// Record swap step
steps.push({
values: [...arr],
compareIndices: [],
swapIndices: [i, j],
sortedIndices: [],
pivotIndex: high,
activeRange: [low, high],
codeLine: 6,
title: "Swapping elements",
explanation: `arr[${j}] ≤ pivot, swapping arr[${i}] and arr[${j}]`,
});
}
}
// Place pivot in final position
[arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
return i + 1;
}
function quickSort(low: number, high: number): void {
if (low < high) {
const pi = partition(low, high);
quickSort(low, pi - 1);
quickSort(pi + 1, high);
}
}
quickSort(0, arr.length - 1);
return steps;
}
The key insight: run the real algorithm, but snapshot state at every meaningful operation. The algorithm runs to completion immediately — generating a flat array of steps. Playback is just incrementing an index through that array. No timers during step generation, no async logic, no complexity. Just a pure function.
System Design Visualizers — The Differentiator
Most DSA visualizers stop at sorting and searching. AlgoLens goes further with system design flows — and this is where it gets interesting.
LRU Cache
The LRU Cache visualizer shows the doubly-linked list and HashMap side by side. Every get and put operation animates both structures simultaneously — you see exactly why DLL + HashMap gives you O(1) eviction.
interface LRUStep {
cacheMap: Map<number, number>; // key → value
order: number[]; // front = most recent
operation: "get" | "put" | "evict";
accessedKey: number;
explanation: string;
}
Consistent Hashing
The Consistent Hashing visualizer renders the hash ring with servers placed at their hash positions. When you add or remove a server, it animates which keys get remapped — and you can see how virtual nodes dramatically improve load distribution.
This directly maps to concepts from my Consistent Hashing blog post. Seeing the ring move makes the theory click in a way reading about it never did.
The Playback Engine
The LessonLab component drives all playback with a simple state machine:
const [currentStep, setCurrentStep] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const [speed, setSpeed] = useState(600); // ms per step
useEffect(() => {
if (!isPlaying) return;
if (currentStep >= steps.length - 1) {
setIsPlaying(false);
return;
}
const timer = setTimeout(() => {
setCurrentStep((prev) => prev + 1);
}, speed);
return () => clearTimeout(timer);
}, [isPlaying, currentStep, speed]);
Clean, predictable, easy to test. The speed slider maps to the speed state — slower speed means longer timeout, giving more time to understand each step.
What I Learned Building This
1. Pure functions are the foundation of testable UI
Every step generator is a pure function — same input always produces the same steps. This made debugging straightforward: if a sorting animation looks wrong, run the step generator in isolation and inspect the output.
2. CSS transitions > JavaScript animations for performance
Animating bar heights with CSS transition: height 0.3s ease is dramatically more performant than JavaScript-driven animations. The browser compositor handles it on a separate thread — the main thread stays free for React re-renders.
3. The explanation panel is as important as the animation
Early versions had the animation without the plain-English narration. Users could see what was happening but not why. Adding explanation to every step transformed comprehension. Visual + verbal together is how understanding sticks.
4. System design visualizers are underserved
Every DSA visualizer does sorting and trees. Almost none touch consistent hashing, LRU cache, or load balancing. These are the concepts that separate Senior from Staff Engineer interviews — and they're the most satisfying to see animated.
What's Next
AlgoLens is actively expanding. On the roadmap:
- Interview Mode — timed prompts with hints, complexity checks, and scoring
- AVL Trees — self-balancing with rotation animations
- A* Search — heuristic pathfinding on a grid
- Coin Change & Edit Distance — completing the DP section
- Message Queue, Rate Limiter, CDN — more system design flows
- Comparison View — run two algorithms on the same input simultaneously
If you find AlgoLens useful, the repo is open source — contributions welcome.
GitHub → github.com/ysumit99/algolens
Try it live → algolens-cyan.vercel.app
Building AlgoLens reminded me why I became an engineer. Not to write code — but to build things that make other people understand things faster, more deeply, more intuitively. That's the goal. Every algorithm, animated.