netspd
A beautiful, modern network speed test for your terminal — with a hypercar tachometer.
netspd measures ping, jitter, download and upload against LibreSpeed-compatible servers and renders them live on a braille-canvas instrument cluster: a spring-loaded needle with an ignition sweep, a heat-gradient band that reddens toward the hatched redline, a session-peak ghost notch, and a latency sub-dial — in the spirit of tools like btop, gitui and lazygit.
Features
- Hypercar tachometer — heat-gradient value band, hatched redline, spring-physics needle with afterglow, ignition sweep before each phase, peak ghost notch, latency sub-dial; twin instrument cluster on wide terminals
- Live metrics — current / average / peak speed, transferred bytes, ETA, elapsed time
- Latency analysis — multiple samples, outlier trimming, jitter, real ICMP packet loss (graceful fallback where ICMP is unavailable)
- Smart server discovery — health-probes the public server list, drops dead servers, auto-selects the nearest
- Headless mode —
--no-tuifor scripts and cron,--jsonfor machine-readable reports - Result history — every run appended as JSON lines to your data directory
- Streaming transfers — nothing buffered in memory; parallel connections with EMA smoothing
- Themes — Default, Nord, Dracula, Catppuccin, Gruvbox, plus your own TOML themes without recompiling
- Responsive layout — adapts from 80×24 up to 4K terminals
- Graceful everywhere — cancellation, retries, timeouts; network failures never panic
Installation
Homebrew (macOS / Linux)
Prebuilt binaries
Download the archive for your platform from the latest release, unpack it and put netspd on your PATH.
From source
Requires Rust 1.88 or newer.
Usage
The test starts automatically: ping → download → upload, then a results summary. If a server fails mid-test, netspd automatically retries with the next-nearest one (unless you pinned a server with --server).
Keyboard
| Key | Action |
|---|---|
q / Esc |
Quit (Esc closes overlays first) |
r |
Restart the test |
g |
Result trends from your history |
s |
Server selection |
t |
Theme selector |
c |
View configuration |
? |
Help |
↑↓ / jk, Enter |
Navigate and confirm in lists |
Scripting
--json prints exactly one JSON object on stdout and nothing else, so it pipes cleanly:
|
Exit code is 0 on a completed test and 1 on any failure. Every completed run (TUI or headless) is also appended to <data dir>/netspd/history.jsonl (macOS: ~/Library/Application Support/netspd/, Linux: ~/.local/share/netspd/), one JSON object per line.
Configuration
netspd reads the first config.toml found in:
$XDG_CONFIG_HOME/netspd/config.toml(macOS:~/Library/Application Support/netspd/config.toml)./config/config.toml
Every key is optional; see config/config.toml for the annotated reference. Highlights:
= "catppuccin" # default | nord | dracula | catppuccin | gruvbox
= 30 # UI frames per second (1..=60)
= "librespeed"
= 1.0
[]
= 10 # length of each transfer phase
= 4 # parallel connections
= 10
[[]] # optional: pin your own backend
= "My Server"
= "https://speedtest.example.com/backend/"
Custom themes go in ~/.config/netspd/themes/*.toml — copy any file from assets/themes/ as a starting point.
Architecture
netspd follows clean architecture with strict one-way dependencies:
┌──────────────────────────────────────────────┐
│ tui/ presentation (Ratatui only) │
│ theme · animation · widgets · screens │
├──────────────────────────────────────────────┤
│ app/ application │
│ events → controller → state → renderer │
├──────────────────────────────────────────────┤
│ engine/ domain (no UI imports) │
│ Engine → scheduler → providers/network │
│ metrics: EMA · sampler · throughput · │
│ latency · statistics │
├──────────────────────────────────────────────┤
│ config/ · errors/ · utils/ infrastructure │
└──────────────────────────────────────────────┘
- The engine emits strongly-typed
EngineEvents over a channel and never imports Ratatui — it is directly reusable from a CLI, GUI, REST API or as a library. - Providers implement one trait (
Provider); adding a new speed test network touches nothing else. - State is plain data mutated only by reducers; the renderer just draws it.
- Metrics are pure, deterministic calculators, each independently unit-tested.
Development
See CONTRIBUTING.md for the layering rules and PR checklist.
Roadmap
- JSON result export and
--no-tuiCLI mode - Result history (JSON lines)
- Trend sparklines over the stored history (
g) -
--server,--list-servers,--duration,--connectionsflags - Automatic failover to the next server on failure
- Packet loss via ICMP echo probing
- Homebrew tap
- Ookla and Fast.com providers
- Scheduled repeat testing