01 Making a link worse
Added latency
Delay applied to every packet is a different quantity from capacity, and it is usually the one that makes software feel broken.

Delay is the quantity that breaks software
A slow link is frustrating; a delayed link is something else. When you reduce capacity, a large file takes longer to arrive. When you add latency, every message in a conversation has to wait — the request, then the acknowledgment, then the reply, then its acknowledgment — and those round trips stack up in ways that no amount of extra bandwidth can fix. These are genuinely different quantities, and confusing them is one of the most common mistakes in testing software against poor network conditions.
Capacity is measured in bits per second. Latency is measured in time — milliseconds, usually — and it represents how long a single packet takes to travel from sender to receiver. The number that actually governs interactive software is the round-trip time: the delay out plus the delay back. A connection with high throughput and high latency will transfer a large video file quickly but make a login prompt feel unresponsive, cause a database query to time out, and stall a TLS handshake that requires several round trips to complete. The file moves at full speed once it starts; the problem is the pause before it does.
What physical latency looks like and what added latency simulates
On a real network, delay accumulates from several sources: the propagation delay imposed by the speed of light across a physical medium, the transmission delay imposed by link speed, processing delay inside switching hardware, and, most importantly for modern networks, queueing delay — packets waiting behind other packets for their turn on the wire. Propagation delay on a trans-Atlantic fibre link is roughly 30–40 milliseconds one way; a geostationary satellite circuit adds around 270 milliseconds each way just from orbital distance. Those are physical facts that cannot be engineered away.

What a traffic shaper does when it "adds latency" is hold packets in a queue for a configured duration before forwarding them. The mechanism is simple: a packet arrives, the shaper timestamps it, and it sits in a delay buffer until enough wall-clock time has passed. The result is a link whose round-trip time is predictably and repeatably elevated — which is exactly what a developer needs when checking whether their software is usable from a rural area, a mobile edge, or the far side of an ocean. The shaper is not degrading signal quality; it is not corrupting bits; it is imposing pure waiting. That isolation — latency without anything else — is what makes it a useful variable to control independently.
In practice, latency is rarely applied completely alone. Real links do not have perfectly constant delay; they exhibit jitter, which is variation in that delay from packet to packet. A connection with 80 ms average round-trip time might deliver individual packets in anywhere from 60 ms to 140 ms, depending on competing traffic, hardware queues and scheduler behaviour. Jitter matters because applications that buffer audio or video must accommodate it or stutter, and protocols that depend on packet ordering can misread a large jitter spike as a loss event. Testing with flat, constant latency is better than nothing, but testing with realistic jitter is closer to what users actually experience.
How congestion control is entangled with the delay
The reason added latency is not merely inconvenient but deeply disruptive runs through the way TCP's congestion control was designed. Van Jacobson's landmark 1988 work — published in ACM SIGCOMM Proceedings that year — gave TCP the ability to detect congestion and back off before a network collapsed entirely. The mechanism relies on round-trip timing: the sender watches how long acknowledgments take and adjusts its sending window accordingly. When that timing signal becomes slow, uncertain or noisy, the sender's decisions degrade with it.
Add 200 ms of one-way latency and the round-trip time of any connection on that link rises by at least 400 ms. TCP's window-based throughput scales inversely with round-trip time; the longer the round-trip, the fewer bytes can be in flight before the sender must wait for acknowledgment. This is why a satellite internet user with nominally high bandwidth often gets substantially lower throughput in practice — the latency is eating into the window size the protocol can use. The interaction between added latency and TCP's control loop means that what looks like a latency test is also always, somewhat, a throughput test too.
For application-layer protocols the picture is more direct. HTTP/1.1 opens a connection, sends a request, waits for a response, sometimes sends another. Each of those waits is a round trip. Multiply by latency. HTTP/2 and HTTP/3 were designed partly to amortise this cost through multiplexing and head-of-line blocking reduction, but they cannot repeal the speed of light, and neither can they help a protocol that does not speak HTTP at all. DNS resolution, TLS negotiation, API calls that chain: any application whose correctness depends on completing a sequence of requests in bounded time will break at high latency in a way that no capacity increase can rescue.
Bufferbloat as a source of latency that was not designed in
There is a category of latency that is not physical, not configured, and not deliberate — and understanding it is essential background. When memory became cheap in the 1990s and 2000s, network equipment was fitted with large buffers as a straightforward way to absorb traffic bursts. The intention was to reduce packet loss. The effect, documented and named by Jim Gettys around 2010, was bufferbloat: when a link is saturated, those large buffers fill with packets, and packets wait inside them for hundreds of milliseconds before being delivered. A user simultaneously downloading a large file and making a voice call would find the call destroyed not by loss but by the delay imposed by that full buffer — latency that appeared without anyone choosing to add it.
Dave Taht and others at Bufferbloat.net drove this work into deployed implementations, including the fq_codel discipline that shipped in the Linux kernel.
The research and engineering response to bufferbloat centred on active queue management algorithms designed to keep queues short by dropping or marking packets early rather than letting buffers fill. Kathleen Nichols and Van Jacobson's CoDel algorithm, described in a 2012 paper published by ACM Queue, explicitly targets standing latency — the persistent, non-transient component of queue delay — rather than instantaneous queue length. Dave Taht and others at Bufferbloat.net drove this work into deployed implementations, including the fq_codel discipline that shipped in the Linux kernel.
The relevance to deliberate latency addition is this: a shaper that adds latency on top of a link that is already accumulating bufferbloat latency will produce conditions that are worse and less predictable than intended. Understanding where the delay comes from in a real network — and what is already baked into the equipment in a test environment — is necessary before deciding how much artificial latency to add. If the goal is to simulate a 150 ms round-trip, and the test environment already contributes 60 ms of bufferbloat, the number to configure is not 150.
Slowy, the Mac utility that gave this site its name, applied latency along with bandwidth limits and loss — the three controls that together describe most of what a bad connection feels like. What it was doing was holding packets in a delay buffer, the same mechanism that any kernel-level shaper uses. The interface simplified the configuration; the mechanism was identical. Understanding the mechanism is what makes it possible to reason about the result rather than just observe it.
