Lowest latency in the browser
How the page around the player, fullscreen and the display's refresh rate add to the delay a viewer sees, and what to do when every millisecond counts.
Once a frame reaches the browser, the page decides how quickly it reaches the screen. Most apps never need to think about this. If yours is steered by the person watching (a robot, a vehicle, a remote machine), these few rules take the most off the delay between the camera and the viewer's eyes.
Draw nothing over the video
The browser can hand a video picture to the screen directly only when nothing is drawn on top of it. Any HTML layered over the video (a control bar, a caption, a badge, a semi-transparent panel) makes the browser compose the whole page into one picture first, which adds a step for every frame.
- For anything you steer (robotics, remote control, a remote desktop), put the controls, status and chat beside the video, not on it.
- Where a few milliseconds do not matter (a crowd game, chat over a live event, a watch party), overlays are fine.
<nearglass-player> draws nothing over the video: its mode, status and
buttons are in a bar under the picture.
Fullscreen is the fastest path
Fullscreen, with nothing over the video, is the fastest way to the screen.
In Chrome on Windows, a fullscreen <video> that fills the screen with
nothing above it can become its own layer that the display shows without
composing the page. In glass-to-glass measurements of the same technique
on a Windows desktop (not through the Nearglass service), this path was
clearly faster than drawing to a canvas; the figures are not published.
The player's engine, createPlayer, can take this path in Chrome when you
give it a <video> and tell it when the page goes fullscreen. Make the
whole page fullscreen (document.documentElement), not the video element,
and hide everything else while it is:
const player = createPlayer({ /* ... */ canvas, video }); // video: an empty <video muted playsinline>
fullscreenButton.addEventListener('click', async () => {
player.fullscreen.before();
try { await document.documentElement.requestFullscreen({ navigationUI: 'hide' }); }
catch { player.fullscreen.refused(); }
});
document.addEventListener('fullscreenchange', () => {
if (document.fullscreenElement) player.fullscreen.enter(); else player.fullscreen.exit();
});
addEventListener('resize', () => player.fullscreen.layout());The switch is seamless both ways: the canvas keeps the last frame until the
video has shown its first, and back. Where the browser cannot take this
path, the player keeps drawing on the canvas. It is not available yet with
present: 'worker', play() or <nearglass-player>. See
the player reference for the
video, layout and scanTargetClass options.
The refresh rate matters
A display shows a new picture only at its next refresh. A frame that arrives just after one waits for the next:
| Display | Longest wait | Average wait |
|---|---|---|
| 60 Hz | 16.7 ms | about 8 ms |
| 144 Hz | 6.9 ms | about 3.5 ms |
For the lowest delay, use a high refresh rate display, and the player's
default latency mode, which draws each frame as soon as it arrives rather
than holding it for an even pace
(Presentation modes).
How the player draws today
| Path | Status | When |
|---|---|---|
| Canvas on the page | Verified, the default | every browser that plays MoQ |
Canvas drawn in a worker (present: 'worker') | Available, as an option | keeps drawing off the page's busy main thread |
| Fullscreen video, drawn by the display directly | Beta: in createPlayer, Chrome only; its geometry and the switch checked in software on one machine | page fullscreen with nothing over the video, as above |
The player trying each path on the viewer's own setup and choosing the fastest, and fullscreen video in the worker path, play() and <nearglass-player> | Planned |
None of these paths has been measured glass to glass through the service yet (Performance).