speedy is a terminal speedtest TUI with live gauges, arc speedometers, and sparkline graphs — powered by the Speedtest.net network.

Features
- Live arc speedometer — animated Unicode block bar showing current speed vs 1 Gbps max
- Animated progress gauge — smooth fill bar with gradient color (blue for download, green for upload)
- Live sparkline — rolling history graph of speed variation during the test
- Ping + Jitter — measured before the speed test
- Server auto-selection — picks the closest / lowest-latency Speedtest.net server
- IP + ISP info — displays your public IP and internet provider
- Tokyo Night color palette — purple, blue, green, pink on a dark background
- Single binary — no runtime dependencies, no Docker, no Python
Install
Pre-built binary (recommended)
Download the latest binary for your platform from the Releases page.
# Linux / macOS
chmod +x speedy
sudo mv speedy /usr/local/bin/speedy
# Windows: move speedy.exe to a directory in your PATH
Go install
go install github.com/marcelofrau/speedy@latest
Build from source
git clone https://github.com/marcelofrau/speedy.git
cd speedy
go build -o speedy .
./speedy
Requires Go 1.22+.
Usage
speedy
That's it. No flags needed.
| Key |
Action |
q / Q / Ctrl+C |
Quit |
Metrics
| Metric |
Description |
| Ping |
Round-trip latency to the test server (ms) |
| Jitter |
Variation in ping latency (ms) |
| Download |
Measured download speed (Mbps) |
| Upload |
Measured upload speed (Mbps) |
| Server |
Name, country, and sponsor of the selected test server |
| IP / ISP |
Your public IP address and internet service provider |
Bufferbloat
After the speed test, speedy automatically runs a bufferbloat test — the most useful metric most speed tests skip.
What is bufferbloat?
When your connection is fully loaded (streaming, uploading, gaming), your router's buffers fill up and your latency explodes — even if your raw speed is high. This ruins video calls, gaming, and VoIP even on fast connections.
How speedy measures it:
- Measures a baseline idle ping to the test server
- Saturates the download path with 8 parallel streams while pinging every 200ms for 10 seconds
- Repeats for upload with 4 parallel streams
- Calculates latency degradation (peak-under-load − baseline) and assigns a grade
Grading scale (same as Waveform's test):
| Grade |
Degradation |
| A+ |
< 5 ms — excellent |
| A |
< 30 ms — good |
| B |
< 60 ms — acceptable |
| C |
< 200 ms — poor |
| D |
< 400 ms — bad |
| F |
≥ 400 ms — unusable |
Example results screen:
╭──────────────────────────────────────────────────────╮
│ BUFFERBLOAT │
│ ──────────────────────────────────────────────── │
│ │
│ Baseline 12 ms │
│ │
│ Download A peak 41 ms (+29 ms) │
│ dl latency ▁▁▂▂▃▃▄▄▅▅▆▆▇▇▆▅▄▃▂▁▁▂▃▄▅▄▃▂▁▁ │
│ │
│ Upload B peak 68 ms (+56 ms) │
│ ul latency ▁▁▂▃▄▅▆▇█▇▆▅▄▃▄▅▆▇█▇▆▅▄▃▂▁▁▂▃▄▅ │
│ │
│ Overall B │
╰──────────────────────────────────────────────────────╯
Architecture
speedy/
├── main.go # Entrypoint — tea.NewProgram
├── internal/
│ ├── speedtest/
│ │ └── runner.go # Speedtest sequence: ping → download → upload
│ │ # then hands off to bufferbloat runner
│ ├── bufferbloat/
│ │ └── runner.go # Baseline ping → saturate download + ping
│ │ # → saturate upload + ping → grade result
│ └── tui/
│ ├── model.go # Model struct, Init(), phase constants
│ ├── update.go # Update() — state machine transitions
│ ├── view.go # View() — renders arc, gauge, sparkline,
│ │ # bufferbloat panels and results
│ └── styles.go # Lip Gloss palette (Tokyo Night / Dracula)
The TUI follows the Elm Architecture via Bubble Tea. Both runners execute in the same goroutine (sequentially) and send progress messages to the program via program.Send(), driving state transitions and live re-renders.
State machine:
Idle → Initializing → FindingServer → Ping
→ Download → Upload
→ BloatBaseline → BloatDownload → BloatUpload
→ Done
Dependencies
The bufferbloat runner has no additional dependencies — it uses only Go's standard net/http library.
Roadmap
- Semicircle speedometer with animated needle
-
--server <id> flag to select a specific test server
-
--json output for scripting
- Save results history to
~/.speedy/history.json
- Homebrew tap
-
--no-bloat flag to skip bufferbloat test
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature
- Commit your changes with a clear message
- Open a pull request
Please open an issue first for large changes.
License
MIT © 2025 Marcelo Frau