pikvm/latency/index.html

3668 lines
68 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html><html lang="en" class="no-js"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="What is a KVM latency, where does it come from, and how to measure it?">
<meta name="author" content="Maxim Devaev">
<link rel="canonical" href="https://pikvm.github.io/pikvm/latency/">
<link rel="prev" href="../pass/">
<link rel="next" href="../atx_board/">
<link rel="alternate" type="application/rss+xml" title="RSS feed" href="../feed_rss_created.xml">
<link rel="alternate" type="application/rss+xml" title="RSS feed of updated content" href="../feed_rss_updated.xml">
<link rel="icon" href="../_assets/favicon.ico">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.6">
<title>Latency in KVM systems - PiKVM Handbook</title>
<link rel="stylesheet" href="../assets/stylesheets/main.484c7ddc.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=arial,+sans-serif:300,300i,400,400i,700,700i%7Cmonospace:400,400i,700,700i&amp;display=fallback">
<style>:root{--md-text-font:"arial, sans-serif";--md-code-font:"monospace"}</style>
<link rel="stylesheet" href="../_assets/user.css">
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
<link href="../assets/stylesheets/glightbox.min.css" rel="stylesheet"><script src="../assets/javascripts/glightbox.min.js"></script><style id="glightbox-style">
html.glightbox-open { overflow: initial; height: 100%; }
.gslide-title { margin-top: 0px; user-select: text; }
.gslide-desc { color: #666; user-select: text; }
.gslide-image img { background: white; }
.gscrollbar-fixer { padding-right: 15px; }
.gdesc-inner { font-size: 0.75rem; }
body[data-md-color-scheme="slate"] .gdesc-inner { background: var(--md-default-bg-color); }
body[data-md-color-scheme="slate"] .gslide-title { color: var(--md-default-fg-color); }
body[data-md-color-scheme="slate"] .gslide-desc { color: var(--md-default-fg-color); }
</style></head>
<body dir="ltr">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#what-is-latency-in-kvm" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href=".." title="PiKVM Handbook" class="md-header__button md-logo" aria-label="PiKVM Handbook" data-md-component="logo">
<img src="../_assets/logo.png" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"></path></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
PiKVM Handbook
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Latency in KVM systems
</span>
</div>
</div>
</div>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"></path></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required="">
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"></path></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard="" data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"></path></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix="">
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://github.com/pikvm/pikvm" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"></path></svg>
</div>
<div class="md-source__repository">
pikvm/pikvm
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href=".." title="PiKVM Handbook" class="md-nav__button md-logo" aria-label="PiKVM Handbook" data-md-component="logo">
<img src="../_assets/logo.png" alt="logo">
</a>
PiKVM Handbook
</label>
<div class="md-nav__source">
<a href="https://github.com/pikvm/pikvm" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"></path></svg>
</div>
<div class="md-source__repository">
pikvm/pikvm
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_1">
<label class="md-nav__link" for="__nav_1" id="__nav_1_label" tabindex="">
<span class="md-ellipsis">
Device guides
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_1">
<span class="md-nav__icon md-icon"></span>
Device guides
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../v4/" class="md-nav__link">
<span class="md-ellipsis">
PiKVM V4 Mini &amp; Plus
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../v3/" class="md-nav__link">
<span class="md-ellipsis">
PiKVM V3
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../switch/" class="md-nav__link">
<span class="md-ellipsis">
PiKVM Switch
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../v2/" class="md-nav__link">
<span class="md-ellipsis">
DIY PiKVM V2
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../v1/" class="md-nav__link">
<span class="md-ellipsis">
DIY PiKVM V1
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2">
<label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="">
<span class="md-ellipsis">
Getting started
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
Getting started
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../cheatsheet/" class="md-nav__link">
<span class="md-ellipsis">
Cheat sheet
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../webui/" class="md-nav__link">
<span class="md-ellipsis">
Web UI overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../shortcuts/" class="md-nav__link">
<span class="md-ellipsis">
Sending shortcuts
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../config/" class="md-nav__link">
<span class="md-ellipsis">
Configuring PiKVM
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../auth/" class="md-nav__link">
<span class="md-ellipsis">
Authentication &amp; 2FA
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../faq/" class="md-nav__link">
<span class="md-ellipsis">
FAQ &amp; troubleshooting
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../flashing_os/" class="md-nav__link">
<span class="md-ellipsis">
Flashing OS
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_3">
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="">
<span class="md-ellipsis">
Networking
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3">
<span class="md-nav__icon md-icon"></span>
Networking
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_3_1">
<label class="md-nav__link" for="__nav_3_1" id="__nav_3_1_label" tabindex="0">
<span class="md-ellipsis">
Internet access
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_3_1_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_3_1">
<span class="md-nav__icon md-icon"></span>
Internet access
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../port_forwarding/" class="md-nav__link">
<span class="md-ellipsis">
Port forwarding
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../reverse_proxy/" class="md-nav__link">
<span class="md-ellipsis">
Reverse proxy
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../tailscale/" class="md-nav__link">
<span class="md-ellipsis">
Tailscale VPN
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../cloudflared/" class="md-nav__link">
<span class="md-ellipsis">
Cloudflare tunnel
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../netbird/" class="md-nav__link">
<span class="md-ellipsis">
NetBird VPN
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../wifi/" class="md-nav__link">
<span class="md-ellipsis">
Setting up Wi-Fi
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../modem/" class="md-nav__link">
<span class="md-ellipsis">
Setting up 3G/4G/LTE modem
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../letsencrypt/" class="md-nav__link">
<span class="md-ellipsis">
Let's Encrypt certificates
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" checked="">
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="">
<span class="md-ellipsis">
Video
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Video
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../video/" class="md-nav__link">
<span class="md-ellipsis">
Video modes (WebRTC, Direct)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../webrtc_config/" class="md-nav__link">
<span class="md-ellipsis">
WebRTC configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../edid/" class="md-nav__link">
<span class="md-ellipsis">
Tuning HDMI EDID
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../pass/" class="md-nav__link">
<span class="md-ellipsis">
HDMI passthrough
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Latency
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Latency
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix="">
<li class="md-nav__item">
<a href="#what-is-latency-in-kvm" class="md-nav__link">
<span class="md-ellipsis">
What is latency in KVM?
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#video-pipeline" class="md-nav__link">
<span class="md-ellipsis">
Video pipeline
</span>
</a>
<nav class="md-nav" aria-label="Video pipeline">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1-host" class="md-nav__link">
<span class="md-ellipsis">
1. Host
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2-hdmi-cable" class="md-nav__link">
<span class="md-ellipsis">
2. HDMI cable
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#3-pikvm" class="md-nav__link">
<span class="md-ellipsis">
3. PiKVM
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#31-capturing" class="md-nav__link">
<span class="md-ellipsis">
3.1 Capturing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#32-encoding" class="md-nav__link">
<span class="md-ellipsis">
3.2. Encoding
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#33-queuing" class="md-nav__link">
<span class="md-ellipsis">
3.3. Queuing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#4-network" class="md-nav__link">
<span class="md-ellipsis">
4. Network
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#5-browser" class="md-nav__link">
<span class="md-ellipsis">
5. Browser
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#measuring-the-latency" class="md-nav__link">
<span class="md-ellipsis">
Measuring the latency
</span>
</a>
<nav class="md-nav" aria-label="Measuring the latency">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#internal-timings" class="md-nav__link">
<span class="md-ellipsis">
Internal timings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#total-timings-to-the-browser" class="md-nav__link">
<span class="md-ellipsis">
Total timings to the browser
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_5">
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="">
<span class="md-ellipsis">
Peripheral devices
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Peripheral devices
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../atx_board/" class="md-nav__link">
<span class="md-ellipsis">
ATX board
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../usb/" class="md-nav__link">
<span class="md-ellipsis">
USB configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../usb_pass/" class="md-nav__link">
<span class="md-ellipsis">
USB passthrough
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../audio/" class="md-nav__link">
<span class="md-ellipsis">
Audio / Microphone
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_5_5">
<label class="md-nav__link" for="__nav_5_5" id="__nav_5_5_label" tabindex="0">
<span class="md-ellipsis">
Keyboard &amp; mouse
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5_5">
<span class="md-nav__icon md-icon"></span>
Keyboard &amp; mouse
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../mouse/" class="md-nav__link">
<span class="md-ellipsis">
Mouse modes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../mouse_jiggler/" class="md-nav__link">
<span class="md-ellipsis">
Mouse jiggler
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../pico_hid/" class="md-nav__link">
<span class="md-ellipsis">
Pico HID (USB, PS/2)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../bluetooth_hid/" class="md-nav__link">
<span class="md-ellipsis">
Bluetooth HID
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../msd/" class="md-nav__link">
<span class="md-ellipsis">
Mass storage drive
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../usb_ethernet/" class="md-nav__link">
<span class="md-ellipsis">
Ethernet-over-USB
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../usb_serial/" class="md-nav__link">
<span class="md-ellipsis">
Serial-over-USB
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../gpio/" class="md-nav__link">
<span class="md-ellipsis">
GPIO (pins, relays, lamps, etc)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_6">
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="">
<span class="md-ellipsis">
Advanced usage
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_6">
<span class="md-nav__icon md-icon"></span>
Advanced usage
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../auth_advanced/" class="md-nav__link">
<span class="md-ellipsis">
Advanced authentication
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../id/" class="md-nav__link">
<span class="md-ellipsis">
PiKVM identification
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../on_boot_config/" class="md-nav__link">
<span class="md-ellipsis">
On-boot configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../auto_snapshots/" class="md-nav__link">
<span class="md-ellipsis">
Automatic snapshots
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../vnc/" class="md-nav__link">
<span class="md-ellipsis">
Using VNC
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../multiport/" class="md-nav__link">
<span class="md-ellipsis">
Multiport KVM-over-IP
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../wol/" class="md-nav__link">
<span class="md-ellipsis">
Wake-on-LAN the server
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../ipmi/" class="md-nav__link">
<span class="md-ellipsis">
IPMI &amp; Redfish integration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../pst/" class="md-nav__link">
<span class="md-ellipsis">
Persistent storage
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../prometheus/" class="md-nav__link">
<span class="md-ellipsis">
Prometheus monitoring
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_7">
<label class="md-nav__link" for="__nav_7" id="__nav_7_label" tabindex="">
<span class="md-ellipsis">
Development
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_7_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_7">
<span class="md-nav__icon md-icon"></span>
Development
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../api/" class="md-nav__link">
<span class="md-ellipsis">
HTTP API reference
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../building_os/" class="md-nav__link">
<span class="md-ellipsis">
Building PiKVM OS
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../3d_printing/" class="md-nav__link">
<span class="md-ellipsis">
Cases for 3D printing
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_8">
<label class="md-nav__link" for="__nav_8" id="__nav_8_label" tabindex="">
<span class="md-ellipsis">
Legacy
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_8_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_8">
<span class="md-nav__icon md-icon"></span>
Legacy
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../arduino_hid/" class="md-nav__link">
<span class="md-ellipsis">
Arduino HID
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../msd_legacy/" class="md-nav__link">
<span class="md-ellipsis">
Big DVD images on old PiKVM
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_9">
<label class="md-nav__link" for="__nav_9" id="__nav_9_label" tabindex="">
<span class="md-ellipsis">
PiKVM info
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_9_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_9">
<span class="md-nav__icon md-icon"></span>
PiKVM info
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../compliance/" class="md-nav__link">
<span class="md-ellipsis">
Compliance
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_9_2">
<div class="md-nav__link md-nav__container">
<a href="../blog/" class="md-nav__link ">
<span class="md-ellipsis">
Blog &amp; news
</span>
</a>
<label class="md-nav__link " for="__nav_9_2" id="__nav_9_2_label" tabindex="0">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_9_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_9_2">
<span class="md-nav__icon md-icon"></span>
Blog &amp; news
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_9_2_2">
<label class="md-nav__link" for="__nav_9_2_2" id="__nav_9_2_2_label" tabindex="0">
<span class="md-ellipsis">
Archive
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="3" aria-labelledby="__nav_9_2_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_9_2_2">
<span class="md-nav__icon md-icon"></span>
Archive
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../blog/archive/2026/" class="md-nav__link">
<span class="md-ellipsis">
2026
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../blog/archive/2025/" class="md-nav__link">
<span class="md-ellipsis">
2025
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../blog/archive/2024/" class="md-nav__link">
<span class="md-ellipsis">
2024
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../blog/archive/2023/" class="md-nav__link">
<span class="md-ellipsis">
2023
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../blog/archive/2022/" class="md-nav__link">
<span class="md-ellipsis">
2022
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../blog/archive/2021/" class="md-nav__link">
<span class="md-ellipsis">
2021
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../blog/archive/2020/" class="md-nav__link">
<span class="md-ellipsis">
2020
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_9_2_3">
<label class="md-nav__link" for="__nav_9_2_3" id="__nav_9_2_3_label" tabindex="0">
<span class="md-ellipsis">
Categories
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="3" aria-labelledby="__nav_9_2_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_9_2_3">
<span class="md-nav__icon md-icon"></span>
Categories
</label>
<ul class="md-nav__list" data-md-scrollfix="">
<li class="md-nav__item">
<a href="../blog/category/development/" class="md-nav__link">
<span class="md-ellipsis">
Development
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../blog/category/products/" class="md-nav__link">
<span class="md-ellipsis">
Products
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../blog/category/releases/" class="md-nav__link">
<span class="md-ellipsis">
Releases
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix="">
<li class="md-nav__item">
<a href="#what-is-latency-in-kvm" class="md-nav__link">
<span class="md-ellipsis">
What is latency in KVM?
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#video-pipeline" class="md-nav__link">
<span class="md-ellipsis">
Video pipeline
</span>
</a>
<nav class="md-nav" aria-label="Video pipeline">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#1-host" class="md-nav__link">
<span class="md-ellipsis">
1. Host
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#2-hdmi-cable" class="md-nav__link">
<span class="md-ellipsis">
2. HDMI cable
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#3-pikvm" class="md-nav__link">
<span class="md-ellipsis">
3. PiKVM
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#31-capturing" class="md-nav__link">
<span class="md-ellipsis">
3.1 Capturing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#32-encoding" class="md-nav__link">
<span class="md-ellipsis">
3.2. Encoding
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#33-queuing" class="md-nav__link">
<span class="md-ellipsis">
3.3. Queuing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#4-network" class="md-nav__link">
<span class="md-ellipsis">
4. Network
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#5-browser" class="md-nav__link">
<span class="md-ellipsis">
5. Browser
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#measuring-the-latency" class="md-nav__link">
<span class="md-ellipsis">
Measuring the latency
</span>
</a>
<nav class="md-nav" aria-label="Measuring the latency">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#internal-timings" class="md-nav__link">
<span class="md-ellipsis">
Internal timings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#total-timings-to-the-browser" class="md-nav__link">
<span class="md-ellipsis">
Total timings to the browser
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1>Latency</h1>
<div><p>When you control a remote machine through a KVM-over-IP device, there's always some latency — a gap between what you do and what you see. When the latency is relatively small, using a KVM-over-IP feels natural, but a large latency may feel frustrating.</p>
<p>Currently, <strong>PiKVM offers 35-50 milliseconds of total latency</strong> — a proven lowest end-to-end latency among all other KVM-over-IP devices available on the market.</p>
<p>We've worked hard to eliminate as many bottlenecks as possible to deliver the smallest latency on the market today. This article breaks down where latency comes from in PiKVM's video pipeline, what you can realistically expect, and how to measure it yourself.</p>
<h2 id="what-is-latency-in-kvm">What is latency in KVM?<a class="headerlink" href="#what-is-latency-in-kvm" title="Permanent link"></a></h2>
<p>When you move your mouse in PiKVM, you can see how the remote cursor lags behind the local one (which is indicated by a blue dot). This is <em>latency</em> — the delay between your keyboard/mouse input and the remote host's response to the video.</p>
<p>Latency defines how responsive a KVM-over-IP device <em>feels</em> to users:</p>
<ul>
<li>Less than 30ms feels nearly instantaneous but is impossible to achieve in remote sessions.</li>
<li>3050ms is the next best thing and works for most tasks.</li>
<li>50100ms generally works, but slight delays are visible during rapid movement.</li>
<li>Anything above 100ms is usually perceived as sluggish and undesirable.</li>
</ul>
<div class="admonition tip">
<p class="admonition-title">Our latency measurements</p>
<p>PiKVM has <strong>around 35-50 milliseconds of total latency</strong>, from capture to displaying, with the following conditions:</p>
<ul>
<li><a href="../v4/">PiKVM V4</a> device</li>
<li>Resolution: 1920x1080 at 60Hz</li>
<li>WebRTC H.264 video mode (default)</li>
<li>H.264 kbps = 5000 (default)</li>
<li>H.264 gop = 0 (default)</li>
<li><a href="../video/#boost-pikvm-v4-to-60fps-h264">H.264 boost enabled</a></li>
<li>Access via a local network or good internet connection</li>
</ul>
</div>
<p>Next, we'll explain how we arrived at these numbers and how you can replicate them yourself to verify our results.</p>
<hr>
<h2 id="video-pipeline">Video pipeline<a class="headerlink" href="#video-pipeline" title="Permanent link"></a></h2>
<p>PiKVM works with the physical world: it captures video from a cable, encodes it, and sends it to your browser over a network.</p>
<p><a class="glightbox" data-type="image" data-width="95%" data-height="auto" href="pipeline_latency.webp" data-desc-position="bottom"><img alt="Pipeline Latency" src="pipeline_latency.webp"></a></p>
<div class="highlight"><pre><span></span><code>+--= Host =--+ +-------= PiKVM =-------+ +---= Browser =---+
| | HDMI | | | |
| Source |==========&gt;|--&gt;[Capturing] (17ms) | | [Rendering] |
| 1920x1080 | Cable | | | | ^ |
| 60Hz | | V | | | |
| | | [Encoding] (13ms) | | [Buffering] |
+------------+ | | | | ^ |
| V (~0ms) | Network | | |
| [Queuing]----------&gt;|==========&gt;|--&gt;[Assembling] |
| | | |
+-----------------------+ +-----------------+
</code></pre></div>
<p>Let's go through the stages.</p>
<h3 id="1-host">1. Host<a class="headerlink" href="#1-host" title="Permanent link"></a></h3>
<p>The host generates an image in the video memory to send it to a physical monitor. This time is very negligible. Next, the host takes this image and converts it into a video signal, which is usually sent to the monitor. PiKVM pretends to be a real monitor and provides a set of supported display resolutions and refresh rates (frequencies) measured in Hz for the host to use. The host selects the most appropriate mode to produce a signal to HDMI cable.</p>
<h3 id="2-hdmi-cable">2. HDMI cable<a class="headerlink" href="#2-hdmi-cable" title="Permanent link"></a></h3>
<p>The HDMI cable transmits the data at the frequency set by the operating system. Since it's not part of the PiKVM signal path, we do not account for it either. Even if we did, a 1-meter cable adds approx. 510 nanoseconds of delay. That's also a negligible amount of latency.</p>
<h3 id="3-pikvm">3. PiKVM<a class="headerlink" href="#3-pikvm" title="Permanent link"></a></h3>
<p>Inside PiKVM, the latency accumulates in three stages: capturing, encoding, and queuing. The data transfer between them takes almost zero time due to the use of DMA or a small amount of already compressed video data.</p>
<h3 id="31-capturing">3.1 Capturing<a class="headerlink" href="#31-capturing" title="Permanent link"></a></h3>
<p>This is where we start measuring latency.</p>
<p>A display refresh rate of 60Hz means that 60 frames per second are sent over the HDMI cable. So it takes <code>1s / 60 = 0.017s</code> or 17ms from the beginning to the end of frame. For 24-bit RGB 1920x1080px video, this means that the HDMI cable should able to transmit approx 6MB of video data every 17ms.</p>
<p>Video capture on PiKVM is always the same as the host's frequency. However, it isn't possible to start processing the frame until it is fully received. <a href="../v4/">PiKVM V4</a> can capture the 1080p signal at 60Hz, but <a href="../v3/">V3</a> and DIY devices can handle only 50Hz. Thus:</p>
<ul>
<li>PiKVM V4: <code>1s / 60 = 17ms</code></li>
<li>PiKVM V3/DIY: <code>1s / 50 = 20ms</code></li>
</ul>
<p>In other words, the higher the frequency, the shorter the frame transmission time and the less the latency.</p>
<p>So, this is the first source of the latency: <strong>17ms for 1080p 60Hz video</strong>.</p>
<h3 id="32-encoding">3.2. Encoding<a class="headerlink" href="#32-encoding" title="Permanent link"></a></h3>
<p>PiKVM sends the captured frame to the hardware H.264 encoder. There is no special magic happening here. Encoding can happen with or without boost:</p>
<ul>
<li>With boost: 13ms to encode 60 frames per second for 1080p.</li>
<li>Without boost: 20ms to encode 30 frames per second for 1080p, every second frame is skipped.</li>
</ul>
<p>Thus, with boost we can add another <strong>13ms of encoding</strong> to the total latency. That's 30ms total at this stage.</p>
<h3 id="33-queuing">3.3. Queuing<a class="headerlink" href="#33-queuing" title="Permanent link"></a></h3>
<p>The encoded frames are transmitted to the WebRTC server, split into RTP video packets along with meta information and suggestions about browser settings, and sent over the network as soon as possible.</p>
<p>Own latency at this stage is <strong>less than 1ms</strong>, so still approximately 30ms total so far.</p>
<h3 id="4-network">4. Network<a class="headerlink" href="#4-network" title="Permanent link"></a></h3>
<p>The general rule of thumb here is that the smaller the frame size is, the faster it will be transmitted over the network. The size of each regular frame in PiKVM is only a few kilobytes,
so it all depends on the quality of the connection, geography, and the distance between the PiKVM and you. We cannot influence any of these things.</p>
<p>The encoding parameters that we use by default are optimal for a regular Internet connection. If you reduce the bit rate, you can gain some bandwidth and latency.</p>
<p>A local network adds <strong>less than 1ms latency</strong>. We are still in the 30ms territory here.</p>
<h3 id="5-browser">5. Browser<a class="headerlink" href="#5-browser" title="Permanent link"></a></h3>
<p>The browser assembles the WebRTC RTP stream, buffers it, and then renders the image. All modern browsers use Google's WebRTC stack, and it works almost equally fast. What makes the differences is which H.264 decoders the browser uses (hardware or software), which RTP extensions are implemented, and so on.</p>
<p>In our experience, Chrome or Chromium works best, followed by Safari and Firefox. Brave and other browsers don't always handle real-time streaming, despite the fact that they also could use the Blink or WebKit engine.</p>
<p>To achieve minimal delays, PiKVM uses some special RTP settings here to reduce buffering time to almost zero. However, the browser internals and rendering still add some latency.</p>
<p>Additionally, the physical rendering time of the frame that goes from the graphics card to the display works exactly the same as an HDMI cable to PiKVM (even if you use a laptop for browsing). The higher the refresh rate of your monitor, the lower the latency.</p>
<p>We can assume that <strong>an average of 10-20ms is added here</strong> for decoding and other things, depending on the client's display frequency.</p>
<hr>
<h2 id="measuring-the-latency">Measuring the latency<a class="headerlink" href="#measuring-the-latency" title="Permanent link"></a></h2>
<h3 id="internal-timings">Internal timings<a class="headerlink" href="#internal-timings" title="Permanent link"></a></h3>
<p>Above, we mentioned the many timings that occur in the internal processes of PiKVM.</p>
<p>To make sure that these timings are correct, you can use a special PiKVM profiling tool
that will measure the timings from the moment the first byte arrives in the video capture until the image is sent to the client.</p>
<p>Run on PiKVM (along with opening the stream in the browser):</p>
<div class="highlight"><pre><span></span><code><span class="gp">[root@pikvm ~]# </span>ustreamer-dump<span class="w"> </span>-s<span class="w"> </span>kvmd::ustreamer::h264<span class="w"> </span>--verbose
<span class="go">...</span>
<span class="go">... 1920x1080 ... GRAB=0.017 ~~0.000~~ ENC=0.014 ~~&gt; LAT=0.031 - size=5878</span>
<span class="go">... 1920x1080 ... GRAB=0.017 ~~0.000~~ ENC=0.013 ~~&gt; LAT=0.030 - size=7139</span>
<span class="go">... 1920x1080 ... GRAB=0.017 ~~0.000~~ ENC=0.014 ~~&gt; LAT=0.031 - size=14058</span>
<span class="go">...</span>
</code></pre></div>
<ul>
<li><code>GRAB=</code> - A capture time.</li>
<li><code>~~xxx~~</code> - Time between stages.</li>
<li><code>ENC=</code> - Encoding time.</li>
<li><code>LAT=</code> - Final latency between capturing and before sending H.264 to the client.</li>
</ul>
<h3 id="total-timings-to-the-browser">Total timings to the browser<a class="headerlink" href="#total-timings-to-the-browser" title="Permanent link"></a></h3>
<p>The measurements performed using this method covers a full video processing chain starting from PiKVM capture and finishing in the browser, except the rendering on display. The starting point is the moment when the first byte of the HDMI frame enters the PiKVM video capture chip. This timestamp is saved, and then transmitted via WebRTC to the browser using the RTP extension <a href="http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time">abs-capture-time</a>. The browser extracts the time from the last received frame every second and outputs to the Web UI the difference between the current time and the timestamp.</p>
<p>When using this method, the clocks on the client computer with the browser and on the PiKVM must be precisely synchronized via NTP. Also we recommend to perform measurement in the local network, because when measured over the internet, the clock may be inaccurate due to divergence even with NTP.</p>
<p>Measurements are only possible in Chromium or Chrome because they have support for the required <a href="http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time">abs-capture-time</a> extension.</p>
<p>If you use Linux, you can install <a href="https://chrony-project.org/">chrony</a> for very accurate NTP synchronization. In our case, we used Chromium on Arch Linux, and the preparations were as follows:</p>
<div class="highlight"><pre><span></span><code><span class="gp">[root@localhost ~]# </span>pacman<span class="w"> </span>-Syy
<span class="gp">[root@localhost ~]# </span>pacman<span class="w"> </span>-S<span class="w"> </span>chrony
<span class="gp">[root@localhost ~]# </span>systemctl<span class="w"> </span>stop<span class="w"> </span>systemd-timesyncd
<span class="gp">[root@localhost ~]# </span>systemctl<span class="w"> </span>start<span class="w"> </span>chronyd
</code></pre></div>
<p>Similar on PiKVM:</p>
<ul>
<li>
<p>Start from updating your device:</p>
<details class="note">
<summary>Updating PiKVM OS</summary>
<div class="admonition tip">
<p class="admonition-title">Tip</p>
<p>We recommend updating PiKVM OS only if you have physical access to the device, or in the most extreme cases.
The update process is very reliable, but there is always a small chance that something may go wrong
so reflashing will be required. PiKVM cannot be bricked, but you need physical access to the memory card
for this operation.</p>
</div>
<p>To update, run following commands under the <code>root</code> user:</p>
<div class="highlight"><pre><span></span><code><span class="gp">[root@pikvm ~]# </span>pikvm-update
</code></pre></div>
<p>If you encounter an error like:</p>
<div class="highlight"><pre><span></span><code><span class="gp">[root@pikvm ~]# </span>pikvm-update
<span class="go">bash: pikvm-update: command not found</span>
</code></pre></div>
<p>It's most likely you have an old OS release. You can update the OS as follows:</p>
<div class="highlight"><pre><span></span><code><span class="gp">[root@pikvm ~]# </span>rw
<span class="gp">[root@pikvm ~]# </span>pacman<span class="w"> </span>-Syy
<span class="gp">[root@pikvm ~]# </span>pacman<span class="w"> </span>-S<span class="w"> </span>pikvm-os-updater
<span class="gp">[root@pikvm ~]# </span>pikvm-update
</code></pre></div>
<p>Next time you will be able to use the usual method with <code>pikvm-update</code>.</p>
</details>
</li>
<li>
<p>Install and run <code>chrony</code>:</p>
<div class="highlight"><pre><span></span><code><span class="gp">[root@pikvm ~]# </span>pacman<span class="w"> </span>-Syy
<span class="gp">[root@pikvm ~]# </span>pacman<span class="w"> </span>-S<span class="w"> </span>chrony
<span class="gp">[root@pikvm ~]# </span>systemctl<span class="w"> </span>stop<span class="w"> </span>systemd-timesyncd
<span class="gp">[root@pikvm ~]# </span>systemctl<span class="w"> </span>start<span class="w"> </span>chronyd
</code></pre></div>
</li>
</ul>
<p>Ideally, you should use the same NTP servers on the client and on PiKVM. They can be configured in <code>/etc/chrony.conf</code>.</p>
<p>To take measurements, follow to PiKVM Web UI and add the <code>show_webrtc_latency=1</code> URL parameter like this: <code>https://pikvm/kvmd/?show_webrtc_latency=1</code>. Switch the video mode to WebRTC in the system menu if necessary. After establishing and stabilizing the connection, you will see the calculated delay in the stream window:</p>
<p><a class="glightbox" data-type="image" data-width="95%" data-height="auto" href="webrtc_latency.webp" data-desc-position="bottom"><img alt="Measured WebRTC Latency" src="webrtc_latency.webp"></a></p>
<p>As already mentioned, the measured value does not include the rendering and display time on the physical display with the browser. For 60Hz, it will be +17ms, for 144Hz it will be +6ms. In both cases, you get a latency <strong>around or less than 50ms</strong>.</p>
<p>Our measurements apply to both PiKVM V4 Mini and V4 Plus. <a href="https://pikvm.org/products">Click here</a> to pick the right version for you!</p></div>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"></path></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
<div class="md-copyright__highlight">
Copyright © 2018-2026 Maxim Devaev
</div>
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "..", "features": ["navigation.indexes", "navigation.sections", "navigation.top", "navigation.expand", "search.highlight", "search.share", "search.suggest", "content.code.copy"], "search": "../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../assets/javascripts/bundle.79ae519e.min.js"></script>
<script src="../_assets/add_paragraphs.js"></script>
<script src="../_assets/scroll_to_summary.js"></script>
<script id="init-glightbox">const lightbox = GLightbox({"touchNavigation": false, "loop": false, "zoomable": true, "draggable": true, "openEffect": "zoom", "closeEffect": "zoom", "slideEffect": "slide"});
document$.subscribe(()=>{ lightbox.reload(); });
</script></body></html>