Benchmarks
Headline
Section titled “Headline”| Metric | Result | Conditions |
|---|---|---|
| Glass-to-glass latency, median | 56–57 ms | 720p30 at 1.25 Mbit/s HEVC, two iPhones, home network, relay in Los Angeles, both directions at once |
| Glass-to-glass latency, p95 | 70 ms | Same run, every frame delivered |
| Fan-out, one relay, no loss | 6,000 subscribers at 5.46 Gbit/s | 4 vCPU (GCP n2-standard-4), 4 sockets, 3.88 cores busy |
| Relay efficiency | ~1.3–1.4 Gbit/s egress per vCPU | Consistent across synthetic and real-video runs |
| SFrame cost | 54 µs per 1080p keyframe | AES-256-GCM on device, against a 33 ms frame budget |
Latency by resolution
Section titled “Latency by resolution”Glass to glass at each size the camera publishes, largest first. Co-host sizes (540p and 360p) matter most: shared video calls use smaller boxes, so they publish at 360p–540p.
| Resolution | Typical use | Median glass-to-glass | Status |
|---|---|---|---|
| 4K (2160p30) | Solo host, broadcast | Measuring Oct 9, 2026 | 4K rung added to the Swift SDK camera ladder (opt-in); end-to-end run scheduled |
| 1080p30 | Host / spotlight | Measuring Oct 9, 2026 | Supported (top rung of default camera ladder) |
| 720p30, 1.25 Mbit/s HEVC | Two-person call | 56–57 ms (p95 70 ms) | Measured: two iPhones, home Wi-Fi, relay in Los Angeles, both phones publishing and decoding at once, 90 s, every frame delivered |
| 540p60, 600 kbps | Co-hosting | 69 ms | Measured on a live co-hosted call, same relay: each phone publishing its camera while receiving and decoding the other host’s video |
| 360p30 | Co-host tiles in shared calls | Measuring Oct 9, 2026 | Supported (600 kbps rung) |
| Shared video calls (360p–540p per tile) | Group calls, co-host grids | Measuring Oct 9, 2026 | Closest references: 540p60 at 69 ms; 720p30 with the layout changing every 8 s at 61 ms |
How these numbers sit against other platforms’ published figures: How MOQOM compares.
Glass-to-glass latency
Section titled “Glass-to-glass latency”Glass to glass is the time from light hitting the sender’s camera to the frame being on the receiver’s screen: capture, encode, packetising, the network both ways through the relay, buffering, decode and display — everything a person on a call experiences.
| Devices | iPhone 15 Pro and iPhone Air |
| Relay | GCP Compute Engine, Los Angeles (us-west2) |
| Network | Ordinary home Wi-Fi in Venice Beach, California (about 108 Mbps down, 8 Mbps up), shared with TVs and other devices streaming movies during the runs; mean round trip to the relay 21.8 ms |
| Media | 720p HEVC from the camera, VideoToolbox hardware encode |
| Direction | Both phones publish and subscribe at once |
| Clocks | Both disciplined against NTP; error bar reported beside every reading |
Results
Section titled “Results”| Scenario | Median | p95 | Notes |
|---|---|---|---|
| 720p30 at 1.25 Mbit/s HEVC, both directions, 90 s | 56–57 ms | 70 ms | Every frame delivered |
| 720p30, layout changing every 8 s | 61 ms | — | |
| 540p60 at 600 kbps, co-hosting | 69 ms | — | Same relay; each phone publishing and decoding the other host at once |
xychart-beta
title "Glass-to-glass latency, two iPhones via a relay in Los Angeles (ms) — lab, pending load test"
x-axis ["720p30 median", "720p30 p95", "720p30 + layout changes", "540p60"]
y-axis "Milliseconds" 0 --> 100
bar [57, 70, 61, 69]
Roughly 22 ms of the median is the network round trip to the relay. The rest is the device: capture, hardware encode, decode and the display’s refresh. On this rig, the time left to remove is mostly on the phone, not in the relay.
Relay fan-out
Section titled “Relay fan-out”One publisher, one track, N subscribers. The relay is restarted between sizes. A size is clean only if every subscriber received every object and the kernel dropped no packets; subscriber reports and the relay’s own counters are read together.
| Role | Machine |
|---|---|
| Relay | GCP n2-standard-4 (4 vCPU), us-east1-b |
| Subscribers | 2 × n2-standard-8 load generators, split evenly |
| Publisher | n2-standard-2 |
| Network | One zone, gVNIC, MTU 1460, rmem_max/wmem_max 128 MiB |
| Relay binary | Identical across every run below |
Synthetic traffic: 30 fps of 4,096-byte frames
Section titled “Synthetic traffic: 30 fps of 4,096-byte frames”| Subscribers | Sockets | Clean | Relay cores | Gbit/s | Kernel drops |
|---|---|---|---|---|---|
| 3,500 | 1 | yes | 2.97 | 3.12 | 0 |
| 4,000 | 1 | yes | 3.44 | 3.55 | 0 |
| 4,500 | 1 | no | 3.61 | 2.90 | 340,498 |
| 5,000 | 1 | no | 3.68 | 3.61 | 297,551 |
| 4,000 | 4 | yes | 3.50 | 3.57 | 0 |
| 4,500 | 4 | yes | 3.76 | 4.11 | 0 |
| 5,000 | 4 | yes | 3.82 | 4.59 | 0 |
| 5,500 | 4 | yes | 3.82 | 5.04 | 0 |
| 6,000 | 4 | yes | 3.88 | 5.46 | 0 |
xychart-beta
title "One 4-vCPU relay, 4 sockets: egress with zero loss (lab, pending load test)"
x-axis "Subscribers" [4000, 4500, 5000, 5500, 6000]
y-axis "Gbit/s delivered" 0 --> 6
bar [3.57, 4.11, 4.59, 5.04, 5.46]
xychart-beta
title "Kernel packet drops: 1 socket vs 4 sockets (lab, pending load test)"
x-axis ["4,500 subs, 1 socket", "4,500 subs, 4 sockets", "5,000 subs, 1 socket", "5,000 subs, 4 sockets"]
y-axis "Packets dropped" 0 --> 350000
bar [340498, 0, 297551, 0]
Reading it. With one socket the knee is between 4,000 and 4,500 subscribers: the kernel’s
single receive queue overflows before the CPU runs out. Sharing the port across four
SO_REUSEPORT sockets removes that limit, and the relay stays clean through 6,000 — the largest
size run. 6,000 is the largest size tested, not the ceiling. CPU is close to saturated at
3.88 of 4 cores and the machine’s 10 Gbit/s egress cap was not reached, so the next limit is CPU
and the next step is a larger machine.
Real video: H.264 clip at 24 fps
Section titled “Real video: H.264 clip at 24 fps”Real video is the number to prefer: a keyframe arrives as a burst many times the size of its neighbours, and that burst is where fan-out costs.
| Run | Subscribers | Sockets | Clean | Relay cores | Gbit/s | Kernel drops |
|---|---|---|---|---|---|---|
| A | 1,000 | 1 | yes | 0.46 | 0.57 | 0 |
| A | 2,000 | 1 | yes | 0.85 | 1.12 | 0 |
| A | 3,000 | 1 | no | 1.17 | 1.49 | 53,600 |
| B | 3,000 | 4 | no | 0.99 | 1.34 | 0 |
| C | 2,000 | 1 | yes | 2.09 | 2.78 | 0 |
| D | 2,000 | 4 | yes | 2.00 | 2.66 | 0 |
| D | 2,500 | 4 | no | 2.55 | 3.34 | 0 |
The clip changed between runs B and C: per-subscriber bitrate went from about 0.56 to 1.39 Mbit/s. Compare rows within a run, and compare runs by Gbit/s per core (1.2–1.4) rather than subscribers per core, which depends entirely on bitrate. “Not clean” rows with zero kernel drops are subscriber-side loss; those sizes need profiling before they are quoted.
Not yet measured
Section titled “Not yet measured”- Other MoQ stacks on the same rig. No published glass-to-glass figure for other MoQ relays uses a comparable method, so we make no latency comparison with them.
- Cellular, and phones in different cities.
- Web, Android and desktop clients — benchmarks will be published with each release.
See Methodology for how these numbers are produced.