A production-grade, pedagogical graph algorithm visualizer โ not just for watching algorithms run, but for understanding, comparing, and breaking them.
This isn't another basic BFS/DFS visualizer. This is a teaching platform and experimentation sandbox with:
- 8 algorithms: BFS, DFS, Dijkstra, A*, Bidirectional BFS, Bellman-Ford, Floyd-Warshall, Greedy Best-First
- Step-by-step engine with play/pause/next/prev/rewind
- Live pseudocode sync โ every step highlights the executing line
- Comparison mode โ run two algorithms side-by-side on the same graph
- Heuristic playground โ A* admissibility teaching tool
- 5 graph generators: random, grid/maze, scale-free, adjacency list, adjacency matrix
- Learning mode โ step explanations in plain English
- Expert mode โ clean, fast, no hints
- Negative edges โ Bellman-Ford + cycle detection
- All-pairs shortest paths โ Floyd-Warshall with live NxN matrix
- Analytics panel โ nodes visited, edges relaxed, path cost, memory, wall time
- Visual encoding โ color-coded node/edge states, overlays, distance tables
- Python 3.8+
- Flask
# 1. Install dependencies
pip install flask
# 2. Run the visualizer
python main.py
# 3. Open your browser
# Navigate to http://localhost:5000That's it. The server starts, and you're visualizing algorithms in seconds.
graph_visualizer/
โโโ graph/ # Data layer
โ โโโ node.py # Node + NodeState enum
โ โโโ edge.py # Edge + EdgeState enum
โ โโโ graph.py # Graph container + 5 generators
โ โโโ __init__.py
โ
โโโ algorithms/ # Algorithm layer
โ โโโ step.py # Step snapshot dataclass
โ โโโ bfs.py # Breadth-First Search
โ โโโ dfs.py # Depth-First Search
โ โโโ dijkstra.py # Dijkstra's Algorithm
โ โโโ astar.py # A* (+ 4 heuristics)
โ โโโ bidirectional_bfs.py # Bidirectional BFS
โ โโโ bellman_ford.py # Bellman-Ford + cycle detector
โ โโโ floyd_warshall.py # Floyd-Warshall (all-pairs)
โ โโโ greedy_bfs.py # Greedy Best-First
โ โโโ __init__.py # REGISTRY โ plugin system
โ
โโโ engine/ # Playback engine
โ โโโ stepper.py # Play/Pause/Next/Prev/Rewind
โ โโโ recorder.py # Run recording + analytics
โ โโโ __init__.py
โ
โโโ ui/ # Presentation layer
โ โโโ canvas.py # SVG renderer
โ โโโ controls.py # All UI panels
โ โโโ __init__.py
โ
โโโ main.py # Flask web app
-
Generator-based algorithms โ every algorithm is a generator that
yields aStepat each meaningful event. The stepper callsnext(). Clean, testable, reusable. -
Stateless rendering โ
render_canvas(graph, step)is a pure function. No mutations. State is passed in, SVG comes out. -
Plugin system โ adding a new algorithm is literally: write the generator, add one entry to
REGISTRY. No changes to the UI or engine. -
Step snapshots โ a
Stepis a frozen-in-time picture of everything the visualizer needs: node states, edge states, queue contents, distances, pseudocode line, explanation. -
Separation of concerns โ graph layer doesn't know about algorithms. Algorithms don't know about rendering. Engine doesn't know about Flask. Clean imports top-to-bottom.
- Random (ErdลsโRรฉnyi): adjustable node count + edge probability
- Grid / Maze: 2D grid with random walls (4-connected)
- Scale-Free (BarabรกsiโAlbert): hub-and-spoke topology
- Import from text: adjacency list or adjacency matrix
Pick any of 8 algorithms. The UI auto-adjusts:
- A / Greedy*: heuristic selector appears
- Bellman-Ford: negative-edge warning
- Floyd-Warshall: matrix panel
- โถ Play โ auto-advance at configurable speed
- โธ Pause
- โญ Next โ step forward
- โฎ Prev โ rewind one step (full history buffered)
- โฎ Rewind โ jump to step 0
- โญ Jump to End โ exhaust generator
Speed: Slow (1s/step) | Medium (0.4s) | Fast (0.15s) | Turbo (0.05s)
Node states (color-coded):
- Grey โ unvisited
- Blue โ frontier (in queue)
- Green โ visited (processed)
- Amber โ current (being expanded right now)
- Purple โ on the final path
- Red โ blocked (obstacle)
- Cyan โ source
- Magenta โ target
Edge states:
- Grey โ default
- Amber pulse โ relaxed
- Purple thick โ chosen (on path)
- Faded โ ignored
Overlays (live panels):
- Priority queue contents (Dijkstra / A*)
- Stack contents (DFS)
- Distance array (Dijkstra / Bellman-Ford)
- g/h/f scores (A*)
- NxN matrix (Floyd-Warshall)
The pseudocode panel highlights the line executing at each step. Every algorithm ships its own pseudocode in algorithms/<algo>.py as PSEUDOCODE: List[str].
Learning Mode (default):
- Step explanations in plain English
- "Why did the algorithm choose this node?"
- Tooltips on every panel
Expert Mode:
- Clean UI
- No hints
- Fast execution
Toggle via checkbox in the sidebar.
Run two algorithms on the same graph and see side-by-side metrics:
- Nodes visited
- Edges relaxed
- Path cost
- Wall time
Example: "Is A* really faster than Dijkstra on this graph?"
Winner badges show which algorithm performed better on each metric.
When running A* or Greedy BFS, the playground shows:
- g (actual cost from source)
- h (heuristic estimate to target)
- f = g + h
Teaching moment: "What happens if h overestimates?" โ A* becomes suboptimal. "What if h = 0?" โ A* degrades to Dijkstra.
Built-in heuristics:
- Euclidean โ โ(ฮxยฒ + ฮyยฒ)
- Manhattan โ |ฮx| + |ฮy|
- Octile โ diagonal-aware
- Zero โ h=0 (becomes Dijkstra)
After every run, see:
- Nodes visited โ how many nodes the algorithm explored
- Edges relaxed โ how many edge-weight updates happened
- Path length โ number of edges on the final path
- Path cost โ total weight of the final path
- Total steps โ number of Step objects yielded
- Wall time โ milliseconds to run to completion
- Memory โ approximate peak memory (step buffer size)
Every module has been smoke-tested end-to-end:
cd graph_visualizer
python3 -c "
from graph import Graph
from algorithms import get_algorithm
from engine import Recorder, compare
# generate a graph
g = Graph.generate_random(num_nodes=10, seed=42)
# run BFS
rec1 = Recorder()
rec1.start('bfs', '0', '9', g)
rec1.run_to_completion()
# run Dijkstra
rec2 = Recorder()
rec2.start('dijkstra', '0', '9', g)
rec2.run_to_completion()
# compare
result = compare(rec1, rec2)
print(f'BFS: {rec1.metrics.nodes_visited} nodes')
print(f'Dijkstra: {rec2.metrics.nodes_visited} nodes')
print(f'Winner: {result.winner_nodes}')
"Output:
BFS: 10 nodes
Dijkstra: 10 nodes
Winner: tie
All 8 algorithms, both generators, grid/scale-free/import graph modes, Recorder, compare, and serialisation round-trip โ all green.
-
Write the generator in
algorithms/your_algo.py:def your_algo(graph, source, target): sb = StepBuilder() # ... your logic ... yield sb.build(step_number=0)
-
Add to REGISTRY in
algorithms/__init__.py:REGISTRY["your_algo"] = AlgoInfo( key="your_algo", label="Your Algorithm", fn=your_algo, pseudocode=PSEUDOCODE, tags=["shortest-path"], complexity_time="O(E log V)", )
-
Done. The UI auto-discovers it. No other changes needed.
Add a @classmethod to Graph in graph/graph.py:
@classmethod
def generate_your_graph(cls, **kwargs) -> "Graph":
g = cls()
# ... generate nodes and edges ...
return gWire it into the Flask API in main.py under /api/graph/generate.
In ui/canvas.py, add a new helper to _render_overlays():
def _render_your_overlay(data, config, x, y):
# ... return SVG string ...The overlay data comes from step.overlay["your_key"], which the algorithm populates.
| Feature | This Visualizer | Typical Student Project |
|---|---|---|
| Algorithms | 8 (BFS, DFS, Dijkstra, A*, Bi-BFS, BF, FW, Greedy) | 2-3 (BFS, DFS) |
| Step-by-step | โ Full rewind + play/pause | โ Or forward-only |
| Comparison mode | โ Side-by-side metrics | โ |
| Pseudocode sync | โ Live line highlighting | โ |
| Heuristic playground | โ A* admissibility teaching | โ |
| Negative edges | โ Bellman-Ford + cycle detector | โ |
| All-pairs | โ Floyd-Warshall + live matrix | โ |
| Graph generators | 5 (random, grid, scale-free, import) | 1 (random) |
| Analytics | โ 8 metrics per run | โ |
| Architecture | Clean plugin system | โ Monolithic |
Built with:
- Flask โ web framework
- Python โ language
- SVG โ rendering
- Lots of coffee โ fuel
Ready to start? Run python main.py and open http://localhost:5000