Skip to content
AFM Studio
  • Pages

    AFM Studio
  • Work
  • Lab
  • About
  • CV
  • Contact
  • Projects

    AksaraA searchable library for Otorita IKN's knowledge, where the server checks every reader's access across three levels
  • Daily TaqwaTamper-resistant prayer attendance system for ~2,000 OIKN government employees at Masjid Negara IKN
  • Lantara v2A permit engine for Otorita IKN where administrators add new permit types from a screen, without a developer
  • eKiosk IKNTwo control rooms for Nusantara: kiosk content management and live bus tracking with arrival times that grade themselves
  • E-Monev IKNA monitoring platform for Nusantara's 23-year build, tracking every unit's quarterly progress against 24 national KPIs
  • JDIH OIKNOIKN's public legal document portal, with a controlled publishing workflow and AI summaries that never leave the server
  • Jejak PenanamanA check-in app for IKN tree-planting events that proves each participant was on site with a geofenced selfie
  • Dashboard SAKTIA nightly pipeline and dashboard that replaces OIKN's manual budget downloads for all six of its budget units
  • DoseRxA bedside dose calculator for 127 drugs that rounds each dose to tablets and syrups Indonesian pharmacies sell
  • CubiqA speedcubing trainer with competition-grade timing and a solver I wrote for each of the 8 major puzzles
  • ScimotionAn interactive science library of 86 articles, each explained next to animations the reader can control
  • WA Daily Scrum BotA WhatsApp bot that chases missing standup reports for a 14-person team and flags tasks stuck for days

↑ ↓ to move · Enter to open · Esc to close

Congestion Lab

A packet-by-packet simulator showing why your video call stutters when someone at home starts a download

Screenshots
congestion control algorithms, one shared sender
5
published results checked before every deploy
4
shareable scenarios, each showing one effect
9
packets simulated in about half a second
288k
On this page

The problem

Every internet connection decides, many times a second, how fast it is allowed to send. That decision explains everyday problems: why a video call stutters when someone at home starts a download, or why a distant server feels slow on a fast line. It is usually taught as one picture, a sawtooth line that most people never see being produced.

Four things stay hidden without a simulator. A server 300 ms away recovers from a lost packet six times slower than one 50 ms away, on the same bandwidth. "Slow start" actually doubles speed every round trip. Oversized router buffers add seconds of delay without losing a packet. And sharing a link fairly works only under specific conditions. Congestion Lab is for networking students, engineers who know the words but not the mechanism, and people who teach it.

What I built

Every packet is simulated, and the curves follow

I built a simulator that sends individual packets over a virtual link. They queue at the router, get dropped when it's full, and are acknowledged. The sending speed you see is whatever the algorithm did in response. No curve comes from a formula. A simulator that draws the shape it expects can never surprise you, and the surprises are the point of this project.

Published results decide whether it can ship

A wrong simulator still draws a convincing sawtooth, so I don't trust it by eye. Four independent results from the research literature are checked across ranges of settings, and a failure blocks the deploy. These cover throughput under loss, link use against buffer size, the advantage of nearby servers, and how two senders converge to equal shares. I had two of them passing before building any screen. The rule is that when a check fails, the simulator is wrong, and the tolerance stays where it is.

Five algorithms share one sender

I implemented five congestion control algorithms from their published specifications: Tahoe, Reno/NewReno, CUBIC, Vegas and BBR v1. They all plug into the same sender, so a comparison measures the algorithms and nothing else. On long, fast links every algorithm first looked the same, because loss recovery was the bottleneck. So I added selective acknowledgement, which wasn't in the plan, to make the comparison mean something.

Four charts share one time axis

The main view stacks sending window, router queue, throughput and round-trip time on one time axis. Reading straight down at any moment shows why the sender did what it did. A bufferbloat page runs the same download over a deep buffer and over a queue built to limit delay: throughput barely changes, while delay differs by a factor of ten. After each run, the site writes its findings out in plain sentences in Indonesian or English, with sources.

It shows where the model stops

Some behaviours can't be modelled faithfully here. I list them as gaps and show them wherever they affect a result. One result surprised me. With a simple drop-when-full queue, packet timing sometimes reversed the expected advantage of nearby servers. I moved the fairness checks to a queue that drops at random and kept a test for the surprising effect.

Result

Congestion Lab is live and public, in Indonesian and English. It has nine curated scenarios, each showing one named effect, and every run can be shared by link without an account or server. The simulator handles 288,000 packets in about half a second in a background thread. It uses no simulation or chart library. An automated accessibility audit found zero violations on its five pages at the time, in both languages. I built it in about 10 hours on 6 August 2026.

Under the hoodTechnical detail for engineers
  • Discrete-event engine in lib/sim/ (~3,600 lines): virtual clock, priority queue, link serialisation, queue disciplines, sender and receiver, packet ledger.
  • (config, seed) → SimTrace is byte-identical on any machine: integer-nanosecond time, no Date, no unseeded randomness, and equal timestamps broken by an explicit monotonic sequence number.
  • Packet conservation (sent = delivered + dropped + in flight) is asserted at every event in every test suite.
  • The four oracles: Mathis et al. 1997 throughput, utilisation versus buffer size and BDP, Reno's RTT unfairness ratio, and Chiu & Jain 1989 AIMD convergence. Before any UI, Mathis matched within 13% across 5 loss rates × 4 RTTs, and buffer sizing within 1% across 9 buffer sizes.
  • Every algorithm implements onAck, onLoss, onTimeout and canSend. There is no if (algorithm === 'cubic') in the sender. A fixed-rate CBR sender serves as a reference.
  • SACK follows RFC 2018 and RFC 6675, configurable per flow via sackEnabled, off for Tahoe.
  • Queue disciplines: DropTail, RED and CoDel. Over deterministic DropTail the Floyd–Jacobson phase effect inverts the RTT bias, so fairness oracles run on RED and the phase effect is asserted in tests/oracles/rtt-unfairness.test.ts.
  • A 60-second 100 Mbps run is about half a million packets. Trace hot fields live in typed arrays with side tables and lazy hydration; the simulation runs in a Web Worker with an event budget. Benchmarked at M0: 288k packets in ~0.5 s with flat heap growth.
  • MODEL_GAPS in lib/sim/engine.ts: no RACK or Tail Loss Probe, no delayed ACK, no HyStart, ACKs never queue or drop, fixed one-MSS segments, and a 200 ms RTO floor (Linux) instead of RFC 6298's 1 s.
  • lib/analysis/insight.ts generates the findings sentences in both locales. Fairness uses Jain's index over time and a Chiu-Jain phase diagram.
  • TraceChart measures its container and states distances in device pixels, after 11px labels rendered at about 4px on phones inside a fixed 1000-unit viewBox.
  • bund (3.0:1) and sediment (2.9:1) pass contrast for strokes but not prose, so text-safe -ink variants were added.
  • Zero axe-core violations across all five routes in both locales at M6. 6 routes × 2 locales, 148 tests in 18 files, ~12,100 lines of TypeScript, 34 commits, three runtime dependencies.

Screenshots

Landing Page
Trace
Compare
Fairness
Queue and AQM
Scenarios

Hiring, or building something similar?

I'm open to full-time roles and select freelance work.