The bottleneck
Your laptop and another sender both upload to a server through one router. The router's outgoing link is the bottleneck: it carries only 20 packets per round trip. Neither sender knows that number.
Slow start
TCP keeps a congestion window (cwnd): how many segments, TCP's packets, it may have sent but not yet had acknowledged. Your laptop starts small, at 3 segments: one cell each in the bar in front of it.
Every ACK that comes back lets cwnd grow by one segment, so a whole window of ACKs doubles it. Each round trip (RTT: the time to send and hear back) it goes 3, 6, 12.
Loss
Together the senders now push 28 packets per round trip into a link that carries 20. The router keeps the extra 8 in its queue, a buffer, where they wait, so every packet's delay grows.
Slow start doubles again, to 24. The queue holds only 10 packets, so packets that arrive while it is full are thrown away: a tail drop. Loss is TCP's classic signal of congestion.
The server keeps acknowledging the last segment it got in order. Three duplicate ACKs tell your laptop one is missing, so it resends it at once (fast retransmit) instead of waiting for a timeout.
On a loss, Reno sets ssthresh (the slow-start threshold) to half the window and drops cwnd to it: 24 becomes 12. The other sender lost packets too: 17 becomes 8. CUBIC, Linux's default, cuts to 0.7 instead.
Sharing the pipe
Now cwnd is at ssthresh, so the sender is careful: congestion avoidance adds about one segment per round trip. Your window climbs 12, 13, 14 … 18 until the queue overflows again.
Add one per round trip, halve on loss: this sawtooth repeats. Each halving shrinks the gap between the two windows (4, then 2, then 1 segments), so both senders end up with about equal shares of the pipe.
With ECN, a router whose queue is building marks a packet Congestion Experienced instead of dropping it. The server echoes the mark in its ACKs, and the sender slows down just as for a loss, but nothing was lost.
The numbers, round trip by round trip
;; step 1/10 · bottleneck · round trips 0 you 192.0.2.10 → 203.0.113.10 other 198.51.100.7 → 203.0.113.10 bottleneck link 20 packets per round trip (illustrative) router queue 10 packets nobody tells the senders these numbers: TCP has to probe
The trace is illustrative and counted in whole segments per round trip: a 20-packet link, a 10-packet queue, and both senders losing in the same round. Real losses are spread out and fast recovery briefly inflates cwnd. CUBIC (RFC 9438), the default in Linux, cuts to 0.7 of the window and regrows along a cubic curve, quickly at first and gently near the old peak. Newer algorithms such as BBR model the path's bandwidth and delay instead of waiting for loss.