Why reliability under packet loss, not best case latency, is the number that indicates true performance of a teleoperation platform.
TL;DR. A teleoperation link is only as good as its worst moment, so we tested for the worst moment. We put Adamo, LiveKit and Transitive on one rig, streamed the same camera through each at the same time, and then degraded the network the way a real deployment degrades: fixed round trip delay plus real packet loss. On a clean link all three are close, in the 83 to 100ms range, but as you add loss, there is a clear separation. At 10% packet loss Adamo holds a median of 133ms, LiveKit rises to 183ms, and Transitive climbs to 617ms. Push to 15%, shown in the videos, and Transitive drops the stream entirely, timing out and waiting for a manual resume, while Adamo and LiveKit keep a live picture. The reason is structural. Adamo does not run on WebRTC, and the jitter buffers and recovery behaviour that make WebRTC pleasant for a video call are exactly what come apart when a robot link turns lossy.
The number everyone quotes is the easy one
Every teleoperation vendor publishes a latency figure, and almost all of them are measured on a clean link on a good day. That is the friendliest condition any transport will ever see. It is also not the condition your robots will run in. Cellular handoffs, congested site WiFi, shared uplinks and long backhauls all produce the same thing in the end: packet loss and jitter. So the question that actually decides a deployment is not how fast a platform is when nothing is wrong. It is what happens to the link when the network gets bad, because that is when an operator is most likely to be mid task on a robot that can move, drop a part or block a road.
There is a second problem hiding underneath the first. There is no shared definition of what latency even means. One vendor measures glass to glass, from the photon hitting the sensor to the photon leaving the operator screen. Another measures only the network hop and leaves out capture, encode, decode and display. A third quotes the transport round trip and calls it latency. Some measure on clean office WiFi with a single hop, others on wired loopback. So two companies can publish the same number and be describing completely different things, which means the figures do not actually compare, they only sound like they do. The only way to get numbers that mean something side by side is to measure every platform the same way, on the same rig, at the same point in the pipeline, which is what we did here.
This writeup spends most of its time on the setup and on what happens under loss, because that is where the useful information is.
The setup
Everything below was run on one machine, on one network path, with one source feed fanned out to all three transports simultaneously. The only variable that changes between the numbers is the transport itself.
Source and capture
The source is an Intel RealSense D435i, used here as a standard colour camera. It is connected to the host over USB 3 and capturing at 60fps. The feed is encoded and streamed at 960 by 540. The source also displays a running millisecond clock on a 60Hz monitor, which gives a frame time of 16.7ms and a reference time we can read directly off the glass.

The rig. The RealSense D435i on a tripod, connected over USB 3, points at the laptop running the source clock. The same feed is fanned out to all three transports on screen, with the source timer and the received streams visible side by side.
The two clock method
We measure glass to glass latency the most direct way there is, with no specialised capture hardware, so anyone can reproduce it. We put the source clock and the streamed clock in the same camera frame and read the gap between them. Because both clocks are in one photograph there is nothing to infer and no separate clocks to synchronise. The difference between the two numbers is the latency for that transport at that instant.
Display a running millisecond clock on the source monitor. This is the reference time before transmission.
Point the camera at the monitor so the clock is fully in frame.
Stream the camera feed through the transport under test to a receiving monitor.
Capture a single screenshot that holds both the live source clock and the streamed clock at the same instant.
Read the difference between the two clock values. That difference is the glass to glass latency.
Repeat across the run and read the steady state, not a lucky first frame.
Run the same procedure for Adamo, LiveKit and Transitive on the same hardware and the same path.
How to read one capture. Each screenshot shows the source clock next to the clock delivered by each transport, so every latency is just the gap between two clocks in the same frame. The source reference is the adamo timer, top right. LiveKit is the top left pane, the browser fan out feed. Transitive is the bottom left pane. Adamo is the bottom right pane, the Adamo Fleet Console. The latency for a transport is the source time minus that pane delivered time. For each condition we took 15 captures and report the median of the 15, which keeps a single lucky or unlucky frame from setting the number.
Network shaping
To move from a clean link to a realistic one we shaped the network with Linux tc, the kernel traffic control tool. We held the round trip delay fixed at 50ms and then injected packet loss at three levels: none, 10% and 15%. The same shaping was applied identically to all three transports, so when the numbers separate it is the transport reacting to loss, not a difference in the test. Every run was London to London, a single region path, so the figures reflect the friendly distance case and isolate the effect of loss rather than long backhaul.
Where the milliseconds come from
Glass to glass latency is the sum of a pipeline, and it helps to know which parts are fixed and which part moves. Most of the pipeline is steady. The camera, the USB transfer, the encode, the decode and the display all cost roughly the same whether the network is clean or not. The stage that moves is the transport, and specifically how it handles jitter and lost packets.

A rough glass to glass budget. The first and last stages are bounded by the 60fps camera and the 60Hz display. The transport row is the one that swings from a few tens of ms on a clean link to hundreds of ms, or a dropped stream, under loss.
Why WebRTC moves the most. WebRTC was built for video calls, where a smooth, glitch free picture matters more than a few tens of ms of delay. To deliver that it leans on a jitter buffer and on retransmitting lost packets. When a link is clean those mechanisms barely engage. When a link is lossy they do exactly what they were designed to do: the buffer grows and the receiver waits for missing packets to arrive, trading latency for smoothness. That is the right call for a conference and the wrong call for a robot. LiveKit and Transitive both carry WebRTC as their media transport, so both inherit this behaviour. Adamo runs a purpose built transport instead, with control on dedicated high priority streams, so when bandwidth tightens the video softens before control does.
How much latency is too much
There is no single cliff, but there are well understood bands. The numbers below are for the full glass to glass loop a person is closing by hand.

Variance matters as much as the average. A link that sits at a steady 80ms is easier to operate on than a link that averages 60ms but spikes to 600ms, because a person can adapt to a constant delay and cannot adapt to an unpredictable one. This is the heart of the reliability argument. A platform that holds a stable figure under load is worth more than one that posts a lower best case and then jumps around when the network gets bad. It is also why the loss tests below matter more than the clean ones.
What we compared
Adamo. A low latency robotics SDK you write against in Python, Rust or C, running a custom QUIC transport over a global relay network rather than the conferencing machinery a browser stack carries. Control commands ride dedicated high priority streams, so when bandwidth tightens the video degrades before control does. NVIDIA Jetson compatible, with hardware accelerated video encoding.
LiveKit. An open source platform built on WebRTC, widely used for real time audio and video and increasingly reached for in robotics because it is familiar and easy to start with. It inherits WebRTC strengths for conferencing and, for our purposes, WebRTC behaviour under loss.
Transitive. A web based teleoperation framework that also leans on WebRTC for its media path. It is built for robotics specifically, which makes it a fairer comparison than a pure conferencing stack, but the underlying transport is still WebRTC.
The common thread on the other side of the table is the one that matters: both LiveKit and Transitive carry WebRTC. That is not a criticism of either project. WebRTC is an excellent answer to the question it was designed for. It is simply a different question from the one a robot operator is asking.
The clean baseline
On a clean link, with the delay fixed at 50ms and no induced loss, the honest answer is that all three are usable for a lot of tasks. This is the friendliest case, and it is worth showing precisely because it is where the WebRTC stacks look their best.
Median glass to glass, 15 captures each: Adamo at 83ms, Transitive at 84ms, LiveKit at 100ms. On a clean link the three are close. Adamo and Transitive are effectively tied and LiveKit sits a little behind, but none of this is disqualifying and all three are comfortably usable. We are not trying to win a clean network race here. The clean baseline exists so the loss results have something to be measured against, because that is where the three stop looking alike.

Clean baseline, 50ms RTT, no induced loss. The source clock, top right, reads 96:21.350. LiveKit, top left, and Transitive, bottom left, both sit within tens of ms of the source, and Adamo, bottom right, is closest. On a clean link the three are effectively level, which is the point the median table above is making.
The real test: under packet loss
This is where the platforms stop looking alike. Holding the delay at 50ms, we injected packet loss and watched what each transport did. The clean numbers are in the table for reference. The loss numbers are the point.

Both rows are the median of 15 captures at that level. Each latency is the source clock, top right, minus that transport delivered clock in its own quadrant: LiveKit top left, Transitive bottom left, Adamo bottom right. Behaviour at higher loss, including Transitive timing out at 15%, is shown separately in the videos below.
At 10% loss, the gap opens
Adamo absorbs the loss best, holding a median of 133ms. That is up from 83ms on a clean link, but still inside the range an operator can work in. LiveKit climbs from 100ms to a median of 183ms, into the band where control starts to feel harder. Transitive is the surprise: on a clean link it was around 84ms, effectively tied with Adamo, yet under 10% loss it climbs to a median of 617ms. It stays connected, but a 617ms loop is well past the point where precise manipulation holds together. The clean tie between Adamo and Transitive turns into a sevenfold gap the moment the network gets lossy, and that is the structural cost of WebRTC showing up.

10% packet loss, 50ms RTT. The source clock, top right, reads 50:56.066. Adamo, bottom right, stays closest to the source. LiveKit, top left, sits further back, and the Transitive pane, bottom left, has fallen roughly 600ms behind. Each transport latency is the source time minus that quadrant time.
Seeing it live: the reliability videos
Separately from the still captures above, we recorded the same fan out as video so the behaviour can be watched rather than read off a frame. These clips include a harder 15% loss run, and that is where the difference stops being about milliseconds. Adamo and LiveKit both keep a live picture. Transitive does not degrade gracefully. It times out. In the frame below the Transitive pane, bottom left, has gone white with a Timed out message and a Resume button, and it stays that way. The stream is simply down, and an operator would have to notice and manually bring it back. For a link a human is using to control a moving robot, that is the failure mode that matters most: not a slow picture, but no picture.

15% packet loss, 50ms RTT. The Transitive pane, bottom left, has timed out and gone white with a Resume button. Adamo, bottom right, and LiveKit, top left, are still streaming. Pulled from the reliability test video.
What the videos show
The three clips run the same fan out under sustained loss for 30 seconds each, so you can watch the behaviour rather than read a single frame. Two clips at 10% loss show the steady state clearly: Adamo stays locked to the source clock while the Transitive pane runs visibly behind, several hundred ms back, for the whole clip. The 15% loss clip is the stark one. The Transitive pane is timed out and white for essentially the entire 30 seconds, while Adamo holds a live picture close to the source and LiveKit keeps a slower but present feed. Watched end to end, the clips make the table concrete: under loss this is not three platforms with different latency, it is two platforms still working and one that has dropped the link.
Watch the reliability clips
The three clips are included alongside this document in the videos folder. Click any frame below to open and play that clip in your default player, or open the files directly from the videos folder. Each runs for 30 seconds at a fixed loss level.
Clip 1. 10% packet loss, 50ms RTT. Adamo tracks the source while Transitive runs several hundred ms behind.
Clip 2. 10% packet loss, 50ms RTT, second run. Same pattern: Adamo locked to the source, Transitive lagging.
What this means if you are choosing a platform
If your use case is a browser demo, a low stakes monitoring view, or anything where a few tens of ms of buffering is invisible and an occasional dropout is fine, a WebRTC stack is a reasonable and convenient choice, and both LiveKit and Transitive are credible options. The moment a person is closing a control loop on a robot that can move, the calculus changes. The buffering that makes WebRTC pleasant becomes the latency that makes teleoperation hard, and the recovery behaviour that protects a video call becomes the timeout that drops your link at the worst moment.
That is the line this comparison is really about. On a clean network the gap is a latency gap. Under the loss a real deployment will actually see, it becomes a reliability gap, and the reliability gap is the one that strands an operator. Adamo and Transitive were effectively tied on a clean link, but the more important result is that Adamo degraded the least as the network got worse, while Transitive went from tied to roughly seven times slower at 10% loss and then dropped the stream entirely at 15%.
The teleoperation partner you pick is the one every robot in your fleet will operate on for years. It is worth measuring under bad conditions before you commit, which is exactly why we ran the test this way instead of asking you to take a clean number on faith.
See it for yourself at adamohq.com/teleop.
FAQs
Which teleoperation platform is most reliable under poor network conditions?
In a like for like test on one rig, Adamo was the most reliable of the three under packet loss. On a clean link all three are close, but at 10% loss Adamo held a median of 133ms while LiveKit rose to 183ms and Transitive climbed to 617ms, and at 15% loss the Transitive stream timed out entirely while Adamo and LiveKit kept a live picture. Reliability under loss, not best case latency on a clean link, is what separates teleoperation platforms, because real networks drop packets and a dropped link is what strands an operator mid task. Adamo holds up because it does not run on WebRTC, degrading the video before it degrades control.
Why does WebRTC add latency under poor network conditions?
WebRTC was built for video calls, where a smooth picture matters more than a few tens of ms of delay. To protect that smoothness it relies on a jitter buffer and on retransmitting lost packets, so when a link becomes lossy the buffer grows and the receiver waits for missing packets, trading latency for picture quality. In our tests, 10% packet loss pushed the WebRTC based Transitive stack from about 84ms to roughly 617ms, and at 15% loss it timed out entirely. Both LiveKit and Transitive carry WebRTC, so both inherit this behaviour.
Is Adamo faster than LiveKit and Transitive for robot teleoperation?
On a clean link the three are close, measured glass to glass at 83ms for Adamo, 84ms for Transitive and 100ms for LiveKit. The gap opens under packet loss. At 10% loss Adamo held a median of 133ms while LiveKit rose to 183ms and Transitive climbed to 617ms, and at 15% loss Transitive dropped the stream entirely while Adamo kept running. Adamo achieves this by not using WebRTC, running a purpose built QUIC transport with control on dedicated high priority streams instead.