Perm is a terminal-based sorting analysis platform built on Python's modern async TUI framework (Textual) with high-performance tracing via msgspec and frame-based visualization.
┌──────────────────────────────────────────────────────────────────┐
│ CLI Layer (Typer) │
│ run --viz │ benchmark │ arena │ replay │ export | config │
└─────────────────────────┬────────────────────────────────────────┘
│
┌─────────────────────────▼────────────────────────────────────────┐
│ UI Layer (Textual) │
│ PermApp │ SortScreen │ VizViewport │ FramePlayer │ Timeline │
│ (FramePlayer-driven: play/pause/step/jump/reverse) │
└─────────────────────────┬────────────────────────────────────────┘
│
┌─────────────────────────▼────────────────────────────────────────┐
│ Service Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Algorithm │ │Benchmark │ │ Arena │ │ Replay │ │
│ │ Engine │ │ Engine │ │ Engine │ │ Engine │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────┬────────────────────────────────────────┘
│
┌─────────────────────────▼────────────────────────────────────────┐
│ Core Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Algorithms│ │ Datasets │ │ Tracing │ │ Metrics │ │
│ │ 13 sorts │ │ 8 types │ │msgspec │ │Collector │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Visualizer│ │Exporters │ │ Plugins │ │ Config │ │
│ │ 8 modes │ │ 6 formats│ │ 3 types │ │ TOML │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────────────────────────────────────────────┘
SortAlgorithm ──emit──▶ Tracer ──▶ TraceEvent[]
│
▼
FrameBuffer
(event → Frame)
│
▼
FramePlayer
(play/pause/step/jump)
│
▼
Visualizer.render(Frame)
│
▼
Renderable (Rich)
The sort algorithm emits trace events (compare, swap, write, partition, etc.) via the Tracer. The FrameBuffer converts events into discrete Frame objects (snapshot + element states). The FramePlayer drives playback. Visualizers accept a Frame and produce a Rich renderable.
- Algorithm runs to completion, emitting events to Tracer
- FrameBuffer.process_events() reconstructs array snapshots at each event
- Each Frame captures: array values, per-element states, timestamp
- FramePlayer iterates frames with play/pause/step/jump controls
- Visualizer converts Frame to Rich renderable (Text, Table, etc.)
- Rich Live or Textual displays the renderable in the terminal
- perm.algorithms: Sort algorithm implementations with tracing instrumentation
- perm.datasets: Dataset generators for various distribution patterns
- perm.tracing: High-performance event tracing with msgspec serialization
- perm.metrics: Real-time metrics collection (comparisons, swaps, etc.)
- perm.visualization: Frame-based rendering engine:
frame.py- Frame dataclass, ElementState enum, FrameBuffer (event→frame)player.py- FramePlayer (playback state machine, timeline scrubbing)base.py- Visualizer ABC (render(frame) → RenderableType)bars.py- CAVA-style gradient bar visualizer (vertical + horizontal)dots.py,heatmap.py,block.py,braille.py,numeric.py,motion.pyrenderer.py- Mode-dispatching coordinator
- perm.benchmarking: Statistically sound benchmarking with warmup and outlier removal
- perm.arena: Tournament system with ELO ratings
- perm.replay: Trace replay with play/pause/step controls
- perm.exporters: Multi-format export (JSON, CSV, Markdown, HTML, GIF, PNG sequence)
- perm.plugins: Plugin system for custom algorithms, datasets, and visualizers
- perm.config: TOML-based configuration with Pydantic models
- perm.cli: Typer-based command-line interface (
--vizfor live animation) - perm.ui: Textual-based terminal UI with FramePlayer-driven dashboard
- Frame-based visualization: Algorithms emit events → FrameBuffer builds frames → Visualizer renders. Decouples algorithm logic from rendering; enables frame-by-frame scrubbing, replay, and export.
- msgspec for tracing: High-performance binary serialization for millions of trace events
- Rich for rendering: Battle-tested terminal rendering with full Unicode, gradient color support
- Textual for TUI: Async, non-blocking terminal UI with CSS-like styling
- Pydantic for config: Type-safe configuration with validation
- FramePlayer owns state machine: Separates playback concerns from visualizer rendering
- CAVA-style bars: Gradient colors (blue→cyan→white) with Unicode partial blocks for smooth edges
| Mode | Description | Best for |
|---|---|---|
bars |
CAVA-style vertical gradient bars | General purpose, beautiful default |
horizontal |
Left-to-right gradient bars | Wide datasets, comparison overview |
dots |
Dot plot density visualization | Large datasets (10k+) |
heatmap |
Character-cell heatmap | Pattern recognition |
braille |
High-density braille dot matrix | Terminal-density visualization |
block |
Multi-row block rendering | Quick visual scan |
numeric |
Raw number display | Debugging, small datasets |
motion |
Element movement trails | Understanding swap/merge patterns |
The perm run --viz command uses Rich's Live display to animate through
frames at the specified FPS:
perm run --algorithm QuickSort --dataset Random --size 50 --viz --fps 30
| Option | Default | Description |
|---|---|---|
--viz / --no-viz |
--no-viz |
Enable live animation |
--viz-mode |
bars |
Visualization mode |
--fps |
30 |
Animation speed |