Skip to main content
The interactive visualizers are the heart of DS Visualizer. They transform abstract data structure operations into visual, animated experiences that make learning intuitive and engaging.

Available Visualizers

DS Visualizer currently includes visualizers for:

Stack

LIFO structure with push, pop, and peek operations

Queue

FIFO structure with enqueue and dequeue operations

LinkedList

Singly-linked list with insertion and deletion at any position

Binary Tree

Binary search tree with insertion and traversal

Heap

Min/max heap with heapify operations

HashMap

Hash table with collision handling

HashSet

Set implementation with hash-based storage

Priority Queue

Queue with priority-based ordering

Graph

Graph representation and traversal algorithms

Visualizer Features

Every visualizer is built with consistent, intuitive features:

Control Panel

Each visualizer includes an OptionBackground component with:
Text inputs for entering values:
<Input ref={inputRef} content="Enter the value you want to add:" />
  • Auto-focus on page load
  • Integer validation (for numeric data structures)
  • Clear after submission
  • Custom validation per data structure (e.g., bracket-only input for stack examples)

Animation System

DS Visualizer uses Framer Motion to create smooth, meaningful animations:
Handles enter and exit animations for dynamic elements:
<AnimatePresence>
  {stack}
</AnimatePresence>
What it does:
  • Animates elements when they’re added to the array
  • Animates elements when they’re removed
  • Waits for exit animations to complete before unmounting
Example: When you pop from a stack, the top element fades out and slides up before disappearing.
Creates smooth layout transitions when elements reposition:
<AnimateSharedLayout>
  <AnimatePresence>{nodes}</AnimatePresence>
</AnimateSharedLayout>
What it does:
  • Tracks position changes of elements
  • Animates smooth movement between old and new positions
  • Maintains visual continuity during reordering
Example: When you insert a node in the middle of a linked list, all subsequent nodes smoothly slide to make room.
Individual elements wrapped in motion.div for custom animations:
<motion.div
  initial={{ opacity: 0, scale: 0.8 }}
  animate={{ opacity: 1, scale: 1 }}
  exit={{ opacity: 0, scale: 0.8 }}
  transition={{ duration: 0.3 }}
>
  {value}
</motion.div>
Common animation patterns:
  • Scale: Elements grow in when added, shrink when removed
  • Opacity: Fade in/out for smooth appearance changes
  • Position: Slide movements for insertions and deletions
All animations are designed to be meaningful - they help you understand what is happening to the data structure, not just provide visual flair.

Visual Indicators

Each visualizer includes helpful visual cues:

Stack Visualizer

<motion.div
  className="flex flex-col-reverse items-center mt-10 px-3 py-3 
             border-2 border-purple-500 border-t-0 min-h-[0.8rem] w-28"
>
  <AnimateSharedLayout>
    <AnimatePresence>{stack}</AnimatePresence>
  </AnimateSharedLayout>
</motion.div>
<div className="text-gray-300">
  Count: <span className="text-white">{stack.length}</span>
</div>
Features:
  • flex-col-reverse - Stack grows upward (new items at top)
  • Purple border - Represents stack container
  • No top border - Shows stack is “open” at the top
  • Count display - Shows current number of elements

LinkedList Visualizer

<div className="flex space-x-3 overflow-hidden">
  <AnimateSharedLayout>
    <AnimatePresence>{nodes}</AnimatePresence>
  </AnimateSharedLayout>
</div>
Features:
  • Horizontal layout - Nodes arranged left to right
  • Arrow indicators - Show pointer connections
  • head and tail labels - Mark special positions
  • Null node - Visual terminator at the end

Binary Tree Visualizer

<div className="w-screen min-h-[50vh] overflow-hidden flex justify-center pt-20">
  <AnimateSharedLayout>{tree}</AnimateSharedLayout>
</div>
Features:
  • Hierarchical positioning - Parent nodes above children
  • Line connections - Show parent-child relationships
  • Automatic balancing - Layout adjusts based on tree structure
  • Center alignment - Root node always centered

Node Rendering

Each data structure defines a Node class with a render() method:
class Node {
  value: number;
  
  constructor(value: number) {
    this.value = value;
  }
  
  render(): JSX.Element {
    return (
      <motion.div
        key={Math.random()}
        initial={{ opacity: 0, scale: 0.8 }}
        animate={{ opacity: 1, scale: 1 }}
        exit={{ opacity: 0, scale: 0.8 }}
        className="bg-purple-500 text-white px-4 py-2 rounded"
      >
        {this.value}
      </motion.div>
    );
  }
}
The render() method returns fully-formed JSX with Framer Motion animations. This encapsulates both the data and its visual representation.

State Management

Visualizers use React hooks to manage state:

Custom Hooks Pattern

const useStack = () => {
  const [stack, setStack] = useState<Array<JSX.Element>>([]);
  const inputRef = useRef<HTMLInputElement>(null);
  const [emptyStack, setEmptyStack] = useState<boolean>(false);

  const push = (value: number) => {
    const node = new Node(value);
    setStack((prev) => [...prev, node.render()]);
  };

  const pop = () => {
    const nodes = stack.slice(0, -1);
    setStack(() => nodes);
  };

  const empty = () => {
    setEmptyStack(true);
  };
  
  return { stack, methods: { push, pop, empty }, refs: { inputRef } };
};
Why this pattern?
  • Separation of concerns: Logic separated from UI
  • Reusability: Hook can be used across different components
  • Type safety: TypeScript ensures correct usage
  • Clean API: Component just calls push(value) without knowing implementation

Real-Time Rendering

Visualizers re-render on every state change:
const renderList = () => {
  let current = First;
  let arr: JSX.Element[] = [];
  
  while (current) {
    arr.push(current.render());
    current = current.next;
  }
  
  const nullNode = new NullNode();
  arr.push(nullNode.render());
  
  setNodes(arr);
};
This pattern traverses the actual data structure (like a linked list) and builds an array of JSX elements. React then efficiently renders only what changed.

Toggle Functionality

Every visualizer includes a toggle to switch views:
const [code, setCode] = useState(true);

<button
  onClick={() => {
    setCode((prev) => !prev);
  }}
  className="fixed z-20 rounded top-20 right-5 px-4 py-2 bg-black text-white"
>
  {code ? "Explanation" : "Code"}
</button>

{code ? (
  <Content html={codeHTML} />
) : (
  <div>
    <Options {...operations} />
    <div className="flex space-x-3">
      <AnimatePresence>{nodes}</AnimatePresence>
    </div>
  </div>
)}
Toggle behavior:
  1. Button is fixed in top-right corner (always accessible)
  2. Label shows the next view (“Explanation” when showing code)
  3. State switches between code walkthrough and interactive visualizer
  4. All component state (like stack contents) is preserved during toggle

Example Problems

Some visualizers include example problem-solving modes:

Stack: Balanced Parentheses

1

Input Phase

Enter a string of brackets: "({[]})"
  • Only bracket characters allowed: ( ) { } [ ] < >
  • Validation prevents other characters
2

Execution Phase

Click “Start” then “Next Step” to process character by character:
  • Open bracket? Push to stack (visual: element appears)
  • Close bracket? Check if it matches top of stack
    • Match: Pop from stack (visual: top element disappears)
    • No match: Show error message and reset
3

Validation Phase

When string is empty:
  • Stack empty? Balanced parentheses ✓
  • Stack not empty? Unbalanced parentheses ✗
Visual feedback appears for 5 seconds before auto-reset
const next = () => {
  if (str === "") {
    if (stack.length === 0) {
      setPopupMsg("Its a balanced parenthesis");
    } else {
      setPopupMsg("Not balanced - stack isn't empty");
    }
    return;
  }
  
  const bracket = str[0];
  
  if (openBrackets.includes(bracket)) {
    const node = new Node(bracket);
    tempStack.push(bracket);
    setStack((prev) => [...prev, node.render()]);
    setStr((prev) => prev.slice(1));
  }
  
  if (closeBrackets.includes(bracket)) {
    const openBracket = bracketPair[bracket];
    const topBracket = tempStack[tempStack.length - 1];
    
    if (topBracket !== openBracket) {
      setPopupMsg(`Close bracket ${bracket} doesn't match top bracket ${topBracket}`);
      return;
    }
    
    tempStack.pop();
    setStack((prev) => prev.slice(0, -1));
    setStr((prev) => prev.slice(1));
  }
};

Responsive Design

Visualizers adapt to different screen sizes:
  • Desktop: Side-by-side explanation panels
  • Mobile: Stacked layout with scrolling
  • Overflow handling: Horizontal scroll for wide structures (linked lists)
  • Vertical scroll: For tall structures (deep trees, full stacks)
Some visualizers may have limited functionality on very small screens. For the best experience, use a device with at least a tablet-sized screen.

Performance Optimizations

Only visible elements are fully rendered. Off-screen nodes use lightweight placeholders.
Rapid operations (like emptying a stack) are batched to prevent animation overload.
Static content (code blocks, explanations) use React.memo to prevent unnecessary re-renders.

What’s Next?

Code Explanations

Learn about the detailed code walkthroughs and complexity annotations

Contributing Guide

Learn how to contribute to DS Visualizer