Adaptive jitter buffers are where most anti-cheat-adjacent netcode solutions get it wrong, so let me be direct: a static buffer of 40-60ms is fine for casual play but actively fights you in ranked. The real win comes from a dynamic buffer that tracks your RTT percentile distribution over a rolling window (I use ~5 seconds at 200 samples). You want to size the buffer based on the 95th percentile jitter, not the average, because a few occasional spikes will otherwise blow past your smoothing window and cause visible rubber-banding. Implement it with an exponential moving average on the inter-arrival delay, and critically, make the decay rate asymmetric—react fast to increased jitter (shrink detection within ~200ms) but ease buffer growth slowly so you aren’t thrashing the latency up and down. That asymmetry is what separates a buffer that feels responsive from one that hammers your ping.
On the smoothing architecture side, I’d recommend decoupling the interpolation delay from the render pipeline timestamp rather than doing naive frame-time offsetting, which introduces stutter on variable framerates. A dead reckoning layer combined with client-side prediction reconciliation works well, but keep your interpolation lag configurable in real-time—I keep mine at 80ms base and can drop to 50ms on a wired connection or bump to 120ms on WiFi. One thing people overlook: measure jitter at the packet level using sequence numbers and inter-arrival timestamps on the client, not just ping samples, because ICMP echo doesn’t reflect UDP game traffic behavior. Also, if you’re on a shared network path, a QoS
"Street Fighter 6 Master Rank (Classic Controls) • Hitbox Controller"