Skip to content

paulohrodrigues/algorithm-visualizer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Algorithm Visualizer

Step-by-step visual animations of classic sorting, searching, tree and data-structure algorithms, built with vanilla JavaScript (ES modules) and served by Node.js + Express.

Every algorithm is a class. Registering it makes its category tab, its button, its info badges, its legend and its animation appear automatically — the UI adapts to the registry.

Running

npm install
npm start

Opens at http://localhost:4000/algorithms.html. Override the port with PORT=xxxx npm start.

It must be served over HTTP (the code uses ES modules, which browsers refuse to load from a file:// path), so open it through the server rather than double-clicking the HTML.

Algorithms included

The category tabs and algorithm buttons are generated from the registry, so this list is exactly what ships today:

Sorting

Algorithm Time Space Idea
Bubble Sort O(n²) O(1) Swaps adjacent out-of-order pairs; the largest value bubbles to the end each pass.
Selection Sort O(n²) O(1) Selects the minimum of the unsorted part and grows a sorted prefix one slot at a time.
Insertion Sort O(n²) O(1) Inserts each element into its correct place in the already-sorted prefix.
Merge Sort O(n log n) O(n) Recursively splits the array in half, then merges the sorted halves.
Quick Sort O(n log n) O(log n) Partitions around a pivot (≤ left, > right), then recurses on each side.

Searching

Algorithm Time Space Idea
Linear Search O(n) O(1) Scans left to right until the target is found. Works on any array.
Binary Search O(log n) O(1) On a sorted array, halves the remaining range each step.

Trees (animated as a sequence of insertions)

Algorithm Time Space Idea
Red-Black Tree O(log n) O(n) Self-balancing BST kept balanced by node colors and rotations (cases 1/2/3).
AVL Tree O(log n) O(n) Height-balanced BST kept balanced by the balance factor and LL/RR/LR/RL rotations.

Structures

Structure Time Space Idea
Queue (FIFO) O(1) per op O(n) First-In First-Out: enqueue at the rear, dequeue from the front.
Linked List O(n) O(n) A chain of nodes pointing to the next; demonstrates append and a head-to-tail search.

Project structure

algorithms.html       — page markup only (links the CSS and src/main.js)
server.js             — Express static file server
css/
  base.css            — shell, controls, code block, layout
  array.css           — array stage and cell states
  tree.css            — tree stage, nodes and edges
  linear.css          — linear stage: cells, pointers, arrows
src/
  main.js             — app shell: playback, controls, and wiring the registry into tabs/buttons
  utils.js            — shared helpers (buildClasses, sortedRange, shuffle)
  algorithms/
    Algorithm.js        — abstract base: render + generateSteps + IO contract (sampleInput, canShuffle, param)
    ArrayAlgorithm.js   — base for array-shaped algorithms (implements render)
    TreeAlgorithm.js    — base for tree-shaped algorithms (SVG render)
    LinearAlgorithm.js  — base for linear structures: queue, list (cells + pointers)
    BubbleSort.js  SelectionSort.js  InsertionSort.js  MergeSort.js  QuickSort.js
    LinearSearch.js  BinarySearch.js
    RedBlackTree.js  AVLTree.js
    Queue.js  LinkedList.js
    index.js            — registry (add new algorithms here)

How the animations work

This is the core idea of the whole project, so it's worth understanding before adding anything.

1. The algorithm precomputes the entire animation

There is no live animation loop running the algorithm. Instead, when an algorithm is loaded, main.js calls algorithm.generateSteps(input) once. The algorithm runs to completion and, at every interesting moment — a comparison, a swap, a rotation, an insertion — it pushes a snapshot onto a list. A snapshot describes the full state at that moment, never a delta.

So generateSteps returns a flat array like steps = [step0, step1, …, stepN], where each step is a plain, immutable object.

2. The player just walks an index

Playback never touches the algorithm again. main.js keeps a single index idx into the steps array and the controls only move it:

  • Step → idx++ · ← Back idx-- · ↺ Reset idx = 0
  • ▶ Play a timer that increments idx until the last step (speed controls the delay)
  • every move calls render(steps[idx])

Because each step is a complete, self-contained snapshot, navigation is instant and fully reversible: Back is as cheap as Step and the algorithm is never re-run. render is stateless — given a step it draws exactly that step.

3. Every step carries shared fields

Whatever the visual form, each step includes:

Field Meaning
status the message shown under the stage
line the 1-based pseudocode line to highlight in the code block (-1 = none)
done true on the final step

main.js reads these to update the status line, highlight the active line of code, and update the progress counter — identically for every algorithm.

4. Rendering is delegated per visual form

main.js stays generic: it owns playback, the legend, the badges, the code block and the status, and delegates only the stage (the drawing area) to algorithm.render(stage, step).

Algorithms that look the same share one renderer through a base class, so a renderer is written once per form, not once per algorithm:

Base class Visual form Stage drawn as Used by
ArrayAlgorithm a row of cells <div> cells all sorts + searches
TreeAlgorithm a tree of nodes inline SVG (circles + lines) Red-Black, AVL
LinearAlgorithm a row of cells with pointers/arrows <div> cells + labels Queue, Linked List

Each base sets stage.className and reads its own step fields, described next.

5. Step shapes per form

Array formArrayAlgorithm{ array, classes, status, line, done }

Field Type Meaning
array number[] the values shown in the cells for this step
classes string[] one CSS state per cell: '', key, cmp, swap, sorted, pivot, min, found, out, dim, done

Tree formTreeAlgorithm{ nodes, edges, status, line, done }

Field Type Meaning
nodes [{ id, value, color, column, depth, state }] color is 'red'/'black'; column is the in-order horizontal slot and depth the level (the renderer turns them into x/y); state: '', cmp, new, fix
edges [{ from, to }] parent → child node ids, drawn as lines

Linear formLinearAlgorithm{ cells, pointers, arrows, tail?, status, line, done }

Field Type Meaning
cells [{ value, state }] the boxes in the row; state: '', new, cmp, remove, active, sorted
pointers [{ label, index }] labels drawn above a cell, e.g. FRONT, REAR, HEAD
arrows boolean draw between consecutive cells (linked list)
tail string (optional) trailing marker after the last cell, e.g. null

Legend colors map a cls name to a CSS variable (--cmp, --swap, …) defined in the CSS files, so a legend entry just needs a matching variable.

6. Input and controls are declared, not hard-coded

The shell never branches on category to decide what data to run or which controls to show. Each algorithm declares that itself, on the Algorithm base:

Property Default Meaning
sampleInput [] the data this algorithm animates. The shell copies it and passes it to generateSteps; it is opaque to the shell (only the Shuffle button assumes it's an array). The form bases set sensible defaults (ArrayAlgorithm an unsorted array, TreeAlgorithm an ascending one, …).
canShuffle true whether the shell shows a Shuffle button that reshuffles sampleInput and regenerates. Searches set it to false (their array must stay sorted).
param null an optional interactive scalar { label, value, min, max }. If present, the shell shows a labelled number box and passes its value to generateSteps(input, value). Searches use it for the target.

So category is used only to group algorithms under a tab (categoryLabel is the tab text). It carries no behavior. A brand-new category — say cryptography — works with no change to the shell: give the class its category/categoryLabel, a sampleInput of whatever shape your generateSteps reads (a number to factor, a short string to encode…), a renderer (extend Algorithm or a form base), optionally a param (e.g. a key) and canShuffle = false, then register it. The tab, button, badges, legend, controls and animation all appear on their own.

Adding a new algorithm

1. Create the class

Create src/algorithms/YourAlgorithm.js extending the base for its visual form. For an array-shaped algorithm that's ArrayAlgorithm (rendering is inherited):

import { ArrayAlgorithm } from './ArrayAlgorithm.js';
import { buildClasses, sortedRange } from '../utils.js';

export class HeapSort extends ArrayAlgorithm {
  id            = 'heap';
  name          = 'Heap Sort';
  category      = 'sort';       // groups this under the Sorting tab
  categoryLabel = 'Sorting';    // tab label (only used when the category is new)
  time          = 'O(n log n)';
  space         = 'O(1)';
  desc          = 'Builds a max-heap then repeatedly extracts the maximum.';
  // sampleInput / canShuffle are inherited from ArrayAlgorithm; override only if needed.
  legend        = [
    { cls: 'key',    label: 'root'     },
    { cls: 'swap',   label: 'swapping' },
    { cls: 'sorted', label: 'sorted'   },
  ];
  code = [
    'def heap_sort(a):',
    '    build_max_heap(a)',
    '    for i in range(len(a) - 1, 0, -1):',
    '        a[0], a[i] = a[i], a[0]',
    '        heapify(a, 0, i)',
  ];

  generateSteps(input) {
    const values = [...input], size = values.length, steps = [];
    // run the algorithm, pushing one snapshot per interesting moment
    // (see "Step shapes per form" above for the array shape)
    return steps;
  }
}

For building the classes array, utils.js provides:

buildClasses(size, { [i]: 'key', [j]: 'cmp' })  // array of `size` classes, rest are ''
sortedRange(low, high)                           // { low: 'sorted', ..., high: 'sorted' }

2. Register it

In src/algorithms/index.js, add one import and one entry to REGISTRY:

import { HeapSort } from './HeapSort.js';

export const REGISTRY = [
  // ... existing algorithms
  new HeapSort(),
];

The category tab and algorithm button appear automatically. No other changes needed.

Adding a new category

Set a category key that doesn't exist yet (e.g. 'graph' or 'crypto') and set categoryLabel to the tab label (e.g. 'Graphs', 'Cryptography'). The tab is created automatically, in the order algorithms are registered. Category is just a grouping key — the shell has no per-category logic, so the new category needs nothing else (see Input and controls are declared, not hard-coded above for how a non-array category supplies its own data and controls).

Adding a new visual form (e.g. graphs)

When the new algorithm cannot be drawn by an existing form, create a new base class beside ArrayAlgorithm.js — say GraphAlgorithm.js — that extends Algorithm, implements its own render(stage, step), sets stage.className, and reads its own step fields. Add a matching css/graph.css and link it from algorithms.html. Algorithms of that form then extend GraphAlgorithm. The shell (main.js) and the registry are untouched.

TreeAlgorithm + RedBlackTree (rendered as SVG, category tree) are a worked example of exactly this: they added a whole new visual form without changing the shell.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors