HTTP load testing you can feel. Every request is a particle; watch your server take the hit, live in your terminal.
Project description
wallop
HTTP load testing you can feel. Every request is a particle. Watch them fly across your terminal and slam into your server — successes spark green, 404s ricochet, 500s explode, timeouts dissolve into smoke. Latency is literally flight time.
Think Logstalgia meets oha: a real load generator with real stats (RPS, p50/p95/p99, status codes), rendered as a live physics scene instead of yet another line chart.
One full incident, live in the terminal: healthy traffic → latency creep (particles stalling mid-air) → error storm (red explosions, embers) → meltdown → recovery.
Why
Load tests produce the most dramatic data in software — a server being hammered, degrading, recovering — and we render it as the world's most boring line charts. You stare at a p99 number and feel nothing.
wallop makes load visceral while staying a real tool:
- Latency is flight time. Fast servers feel like a laser stream. Slow servers make particles visibly hang mid-air, piling up in front of the wall.
- Failures are physical. 4xx ricochet off in orange arcs. 5xx detonate with debris, screen shake, and embers that smolder on the ground. Timeouts never reach the wall — they fade into grey smoke.
- The wall is your server's mood ring. It glows green when healthy and burns red as the error rate climbs.
- The numbers are still there. Live RPS, p50/p95/p99, per-class status counts, error rate, in-flight count, and an RPS sparkline — plus a vegeta-style summary when you're done.
It runs over SSH, in any modern terminal, with zero OpenGL, zero browser, one dependency.
Quickstart (60 seconds)
# with uv (recommended)
uv tool install wallop # or: pipx install wallop
# the instant wow — spins up a built-in chaos server and hammers it
# through a scripted incident: healthy → latency creep → error storm
# → meltdown → recovery
wallop demo
# hammer your own service
wallop http://localhost:8080/api/health -c 100 -d 30s
Running from a clone instead:
git clone https://github.com/siam-hossain9/wallop && cd wallop
uv run wallop demo
Press q or Ctrl+C to stop. You get a clean summary either way:
─────────────────────────────────────────────
wallop summary — http://localhost:8080/api/health
─────────────────────────────────────────────
duration 30.0s
requests 45,231 (1,507.7 req/s)
latency p50 12ms p90 33ms p95 48ms p99 130ms
min 4ms max 1.21s
status 2xx 44,903 · 4xx 210 · 5xx 118
data read 12.4 MB
Usage
wallop URL [options] hammer a URL
wallop demo built-in chaos server + load run (the GIF maker)
wallop serve [--port 8089] run only the chaos server, point anything at it
| Option | Default | Meaning |
|---|---|---|
-c, --concurrency |
50 | concurrent workers |
-d, --duration |
until q |
e.g. 30s, 2m, 500ms |
-n, --requests |
— | stop after N total requests |
-r, --rate |
unlimited | cap request rate (req/s) |
-m, --method |
GET | HTTP method |
-H, --header |
— | 'Name: value', repeatable |
-b, --body |
— | request body, or @file |
-t, --timeout |
10 | per-request timeout (seconds) |
-k, --insecure |
off | skip TLS verification |
--fps |
30 | render frame rate |
Reading the scene
| You see | It means |
|---|---|
| Yellow streaks crossing fast | requests completing quickly |
| Particles hovering near the wall | responses are slow (they stall at ~96% until the response lands) |
| Green sparks on the wall | 2xx |
| Orange particles bouncing back under gravity | 4xx |
| Red explosions, screen shake, embers on the ground | 5xx |
| Grey smoke rising mid-field | timeouts / connection errors |
| Wall turning from green to red | recent error rate climbing |
Architecture
┌────────────────────────────────────────────────┐
│ asyncio loop │
│ │
workers │ engine.py ──on_start/on_complete──▶ app.py │
(aiohttp) │ LoadEngine │ │ │
│ · N workers ▼ ▼ │
│ · token-bucket rate stats.py physics.py
│ · timeout/error taxonomy RPS, p50 World: particles,
│ p95, p99 debris, gravity,
│ EMA embers, shake
│ │ │
│ ▼ ▼
│ renderer.py │
│ half-block (▀▄) pixel │
│ framebuffer → one │
│ ANSI write per frame │
└────────────────────────────────────────────────┘
- One process, one event loop. Engine callbacks mutate the world directly — no queues, no threads.
- The renderer doubles vertical resolution with half-block characters and 24-bit color, composing each frame into a single write.
- Physics is decoupled from frame rate (dt-based), so it looks right at any
--fpsand any terminal size, and survives window resizes mid-run. - ~1,300 lines of Python, one runtime dependency (
aiohttp).
Honest limits
- Python load generation comfortably drives a few thousand req/s per core. For saturating a 10 Gbps NIC, use
wrk/vegeta— for watching and understanding realistic load, use wallop. - At high RPS, impact effects are sampled (debris budget) so rendering never lies about your machine's ability to generate load.
Roadmap
--replaymode: pipe an access log (ortail -f) in and watch production traffic instead of generated load- Per-endpoint lanes when hammering multiple URLs
- HTTP/2 + WebSocket targets
- Latency histogram wall: impact height encodes response time
- A
--recordflag that writes asciinema casts for sharing
Development
uv sync # install with dev deps
uv run pytest # 40 tests covering stats, physics, renderer, engine, CLI
Contributions welcome — see CONTRIBUTING.md.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wallop-0.1.0.tar.gz.
File metadata
- Download URL: wallop-0.1.0.tar.gz
- Upload date:
- Size: 8.8 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4b1d06739d3a08ad8c5ec2dc0ae30944d7d0bb1ce91a4cb5d3b2bc73b05b6a19
|
|
| MD5 |
16f47d716e08045780457c5be7227166
|
|
| BLAKE2b-256 |
e5454dfda4d2bdb39326389d90d15381e6ee18519fdf6b30f7925b5f94860c1d
|
File details
Details for the file wallop-0.1.0-py3-none-any.whl.
File metadata
- Download URL: wallop-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0d8947140d553a5ab2786bcccdde00b5642b99828538794898129ef4fdca394
|
|
| MD5 |
d3c39f53564e85d1348427963dd59b7b
|
|
| BLAKE2b-256 |
cadf06eb5910d82bfc4caeee67370c5daacdaef8834d9c631402a9ef9b4e011d
|