# Jitter Desk > A jitter buffer is sized by the SPREAD of arrival times, not by their mean — so a > path that is faster on average can deliver more total delay than a slower, steadier > one. This works out what each of your paths really delivers, and what every packet > of buffer depth costs in packets that arrive too late to play. ## The one thing to know **A packet that arrives early has to WAIT.** That is what a jitter buffer is: a deliberate delay, deep enough that the latest packet you intend to play still arrives before its turn. ```text spread = arrival(cover-th percentile) - arrival(earliest) depth = ceil(spread / packet) packets, because playout is packet-aligned one-way = earliest + depth*packet + packetisation + codec + capture/playout ``` **The mean appears nowhere.** Only the earliest arrival and the tail do. **The consequence nobody expects: a path that is faster on average can deliver more total delay than a slower, steadier one.** A wifi link averaging 27.4 ms with a 57 ms spread ends up behind a fibre link averaging 40.2 ms with a 4 ms spread — 128 ms against 108 ms — because the buffer has to cover the wifi link's worst packet, and the worst packet is what everybody waits behind. **And the trade is exact and never zero.** The depth is already the smallest whole number of packets that covers the spread, so one packet less is by definition too short for the packet it was sized around. Every millisecond of delay you take out is a packet you arrive too late to play. ## Two ordinary links, compared | Path | Mean arrival | Earliest | Spread | Buffer | Delivered | | --- | --- | --- | --- | --- | --- | | fibre, both ends wired | 40.2 ms | 38 ms | 4 ms | 1 packet (20 ms) | 108 ms | | office wifi | 27.4 ms | 18 ms | 57 ms | 3 packets (60 ms) | 128 ms | | 4G, busy cell | 60.5 ms | 45 ms | 105 ms | 6 packets (120 ms) | 215 ms | **The wifi has the lowest mean arrival and does not deliver the lowest delay.** By average the order is office wifi, fibre, both ends wired, 4G, busy cell; by what actually arrives it is fibre, both ends wired, office wifi, 4G, busy cell. The buffer is sized by the SPREAD and the mean appears nowhere in it. ## What a spread costs | Spread | Buffer needed | Which is | | --- | --- | --- | | 0 ms | 1 packet (20 ms) | the shallowest there is; a buffer still holds one packet | | 5 ms | 1 packet (20 ms) | 15 ms more than the spread, because it holds whole packets | | 15 ms | 1 packet (20 ms) | 5 ms more than the spread, because it holds whole packets | | 20 ms | 1 packet (20 ms) | exactly the spread | | 21 ms | 2 packets (40 ms) | 19 ms more than the spread, because it holds whole packets | | 40 ms | 2 packets (40 ms) | exactly the spread | | 57 ms | 3 packets (60 ms) | 3 ms more than the spread, because it holds whole packets | | 100 ms | 5 packets (100 ms) | exactly the spread | | 180 ms | 9 packets (180 ms) | exactly the spread | At 20 ms a packet the depth steps in 20 ms, so a spread of 21 ms costs the same as one of 40 ms. Choosing a shorter packet buys finer steps and more header overhead; choosing a longer one does the reverse. ## What a percentile needs | Coverage | Samples needed | Below that it is | | --- | --- | --- | | 50th | 2 | a coarse rank rather than a tail | | 75th | 4 | a coarse rank rather than a tail | | 90th | 10 | a coarse rank rather than a tail | | 95th | 20 | a coarse rank rather than a tail | | 99th | 100 | the worst sample you happened to catch | | 99.9th | 1001 | the worst sample you happened to catch | Covering the Nth percentile needs 100/(100-N) samples before the figure is distinguishable from the maximum. A 99th percentile over twenty pings IS the maximum, wearing a different name. ## What it is A single page at https://jitter-desk.skillsafe.ai. The engine that computes all of this runs entirely in your browser, needs no account and costs nothing. Writing the report calls a model and needs credits. ## The free engine Everything below is computed client-side in `jitter.js` and sent with every run as `prescan`: - **Per path.** Mean arrival, earliest arrival, spread at the covered percentile, buffer depth in packets and milliseconds, the fixed costs, the delivered one-way delay and its verdict against the budget. - **The ladder.** What each shallower depth would cost in packets arriving too late, counted against the samples rather than estimated. - **The evidence.** Whether the coverage percentile has collapsed to the maximum for want of samples, and how many it would need. - **The comparison.** Both orderings — by mean arrival and by delivered delay — and the count of pairs where they invert. The engine makes no network calls. ## Sheet grammar Two blocks. Blank lines and `#` comments are ignored. ```text CALL name | the support line packet | 20 codec | 20 capture | 10 target | 150 cover | 99 loss | 1 roundtrip | 96 ARRIVALS a1 | fibre to the datacentre | 38 39 40 40 41 41 42 40 39 41 a2 | the office wifi | 18 20 22 19 21 60 20 19 75 21 ``` An ARRIVALS row is `id | what the path is | the one-way delays, in milliseconds`. The delays may be separated by spaces, commas or both — paste them out of whatever measured them. In CALL, `packet` is the packetisation in milliseconds of audio, `codec` is the codec's algorithmic delay, `capture` is capture plus playout, `target` is the one-way budget, `cover` is the percentile of arrivals the buffer must cover, and `loss` is the share of packets it is acceptable to drop for arriving late. `roundtrip` is optional and only decides whether the echo note appears. **`cover` is the most consequential number after the samples themselves.** It decides which packet the buffer is sized for, and the 99th and the 90th can differ by tens of milliseconds on the same path. ## Lanes | Lane | What it decides | Fields | | --- | --- | --- | | `plan` | The buffer before the call | `brief`, `known` | | `check` | What these paths actually deliver | `sheet`, `symptom` | | `paths` | The paths, ranked by what they deliver | `sheet`, `who` | | `trade` | What each packet of depth costs | `sheet`, `priority` | | `deliver` | What changes: the buffer, the path, or the target | `sheet`, `fixed` | `check` is the primary lane. Every lane ships a worked example that costs nothing to read. ## Thresholds | Threshold | Value | What it decides | | --- | --- | --- | | G.114 comfortable | 150 ms | one-way delay below this affects no conversation | | G.114 limit | 400 ms | above this, conversation stops working | | echo audible | 30 ms round trip | above this echo is perceptible and needs cancelling | | tight spread | 10 ms | below this the buffer costs almost nothing | | loose spread | 50 ms | above this the buffer dominates | | deep buffer | 80 ms | worth reporting on its own | | spike factor | 3x | tail this much beyond the middle half | | default packet | 20 ms | assumed when none is stated | | default codec | 20 ms | assumed algorithmic delay | | default coverage | 99th | assumed percentile to cover | ## Severity Severity is fixed by the code, not by the model. **Errors are reserved for a call that cannot work** — a path past the point where conversation functions, or a target no buffer depth can meet. Most of what this page reports is a description of the situation rather than a fault, because most of the time nobody has misconfigured anything: they have measured the wrong quantity. There are 27 codes. `PATH-UNACCEPTABLE` and `NO-DEPTH-MEETS-TARGET` are the only two errors. ## API ``` POST https://api.skillsafe.ai/v1/app-api/run X-App-Key: Content-Type: application/json {"task": "check", "sheet": "CALL\npacket | 20\n...", "symptom": "the faster link feels worse"} ``` The run body **is** the input object. There is no `input` wrapper. Every field is a string. Full documentation: https://jitter-desk.skillsafe.ai/api.html ## What this page cannot do - **This page has not been on the call.** Every figure is arithmetic on delays somebody measured and pasted in. - **Where the samples came from matters more than anything here.** A ping is not an audio packet: it takes a different queue and frequently a different path. - **A percentile needs samples.** A 99th over fifteen pings is the worst of the fifteen, which is a different quantity wearing the same name. - **Real buffers adapt.** This models a fixed depth, which is what a fixed depth would cost. An adaptive buffer trades the same quantities continuously. - **Loss concealment is not modelled.** A late packet is counted as late; what a codec does about it is a question about the codec. ## Source Lanes derived from the `voice-call` skill in https://github.com/steipete/clawdis, which places voice calls. Every call it places has a buffer sized somewhere in the stack, chosen by a rule nobody on the call ever sees. Not affiliated with or endorsed by that repository’s authors.