Buckets:

hf-doc-build/doc-dev / reachy_mini /pr_1321 /en /SDK /javascript-sdk.html
download
raw
80.7 kB
<meta charset="utf-8" /><meta name="hf:doc:metadata" content="{&quot;title&quot;:&quot;JavaScript SDK runtime reference&quot;,&quot;local&quot;:&quot;javascript-sdk-runtime-reference&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Architecture&quot;,&quot;local&quot;:&quot;architecture&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;API Reference&quot;,&quot;local&quot;:&quot;api-reference&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Constructor&quot;,&quot;local&quot;:&quot;constructor&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;State Machine&quot;,&quot;local&quot;:&quot;state-machine&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Properties (read-only)&quot;,&quot;local&quot;:&quot;properties-read-only&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Methods&quot;,&quot;local&quot;:&quot;methods&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Events&quot;,&quot;local&quot;:&quot;events&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Math Utilities&quot;,&quot;local&quot;:&quot;math-utilities&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Advanced: JSON-RPC &amp; daemon admin&quot;,&quot;local&quot;:&quot;advanced-json-rpc--daemon-admin&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Debug logging&quot;,&quot;local&quot;:&quot;debug-logging&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Daemon-side recorded-move playback&quot;,&quot;local&quot;:&quot;daemon-side-recorded-move-playback&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Combined motion + audio&quot;,&quot;local&quot;:&quot;combined-motion--audio&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Record-time audio (sync anchor)&quot;,&quot;local&quot;:&quot;record-time-audio-sync-anchor&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Audio format&quot;,&quot;local&quot;:&quot;audio-format&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Backpressure &amp; cancellation&quot;,&quot;local&quot;:&quot;backpressure--cancellation&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Live pose stream (30 Hz)&quot;,&quot;local&quot;:&quot;live-pose-stream-30-hz&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Robot onboarding &amp; management&quot;,&quot;local&quot;:&quot;robot-onboarding--management&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Security&quot;,&quot;local&quot;:&quot;security&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Prerequisites&quot;,&quot;local&quot;:&quot;prerequisites&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Working examples&quot;,&quot;local&quot;:&quot;working-examples&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2}],&quot;depth&quot;:1}"/>
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/entry/start.DEyGTiAz.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/chunks/B0eJVyyH.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/chunks/ig1MdIhV.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/entry/app.Cr_rB8lm.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/chunks/UY3R06TA.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/chunks/DQ1c5YBO.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/chunks/DsnmJJEf.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/chunks/BZme7LS0.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/nodes/0.Dsp8lSol.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/chunks/CIV6rF2S.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/nodes/9.C2LSJDp_.js" rel="modulepreload">
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/chunks/D_gSuRJI.js" rel="modulepreload">
<!--wb8kxo--><meta name="hf:doc:metadata" content="{&quot;title&quot;:&quot;JavaScript SDK runtime reference&quot;,&quot;local&quot;:&quot;javascript-sdk-runtime-reference&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Architecture&quot;,&quot;local&quot;:&quot;architecture&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;API Reference&quot;,&quot;local&quot;:&quot;api-reference&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Constructor&quot;,&quot;local&quot;:&quot;constructor&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;State Machine&quot;,&quot;local&quot;:&quot;state-machine&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Properties (read-only)&quot;,&quot;local&quot;:&quot;properties-read-only&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Methods&quot;,&quot;local&quot;:&quot;methods&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Events&quot;,&quot;local&quot;:&quot;events&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Math Utilities&quot;,&quot;local&quot;:&quot;math-utilities&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Advanced: JSON-RPC &amp; daemon admin&quot;,&quot;local&quot;:&quot;advanced-json-rpc--daemon-admin&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Debug logging&quot;,&quot;local&quot;:&quot;debug-logging&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Daemon-side recorded-move playback&quot;,&quot;local&quot;:&quot;daemon-side-recorded-move-playback&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Combined motion + audio&quot;,&quot;local&quot;:&quot;combined-motion--audio&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Record-time audio (sync anchor)&quot;,&quot;local&quot;:&quot;record-time-audio-sync-anchor&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Audio format&quot;,&quot;local&quot;:&quot;audio-format&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Backpressure &amp; cancellation&quot;,&quot;local&quot;:&quot;backpressure--cancellation&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Live pose stream (30 Hz)&quot;,&quot;local&quot;:&quot;live-pose-stream-30-hz&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Robot onboarding &amp; management&quot;,&quot;local&quot;:&quot;robot-onboarding--management&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Security&quot;,&quot;local&quot;:&quot;security&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Prerequisites&quot;,&quot;local&quot;:&quot;prerequisites&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Working examples&quot;,&quot;local&quot;:&quot;working-examples&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2}],&quot;depth&quot;:1}"/><!---->
<link href="/docs/reachy_mini/pr_1321/en/_app/immutable/assets/0.tn0RQdqM.css" rel="modulepreload"> <!--[--><!--[0--><!--[--><!--[0--><!--[--><p></p> <div class="items-center shrink-0 min-w-[100px] max-sm:min-w-[50px] justify-end ml-auto flex" style="float: right; margin-left: 10px; display: inline-flex; position: relative; z-index: 10;"><div class="inline-flex rounded-md max-sm:rounded-sm"><button class="inline-flex items-center gap-1 h-7 max-sm:h-7 px-2 max-sm:px-1.5 text-sm font-medium text-gray-800 border border-r-0 rounded-l-md max-sm:rounded-l-sm border-gray-200 bg-white hover:shadow-inner dark:border-gray-850 dark:bg-gray-950 dark:text-gray-200 dark:hover:bg-gray-800" aria-live="polite"><span class="inline-flex items-center justify-center rounded-md p-0.5 max-sm:p-0 hover:text-gray-800 dark:hover:text-gray-200"><svg class="sm:size-3.5 size-3" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----></span> <span>Copy page</span></button> <button class="inline-flex items-center justify-center w-6 max-sm:w-5 h-7 max-sm:h-7 disabled:pointer-events-none text-sm text-gray-500 hover:text-gray-700 dark:hover:text-white rounded-r-md max-sm:rounded-r-sm border border-l transition border-gray-200 bg-white hover:shadow-inner dark:border-gray-850 dark:bg-gray-950 dark:text-gray-200 dark:hover:bg-gray-800" aria-haspopup="menu" aria-expanded="false" aria-label="Open copy menu"><svg class="transition-transform text-gray-400 overflow-visible sm:size-3.5 size-3 rotate-0" width="1em" height="1em" viewBox="0 0 12 7" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1L6 6L11 1" stroke="currentColor"></path></svg><!----></button></div> <!--[-1--><!--]--></div><!----> <!--[0--><h1 class="relative group"><a id="javascript-sdk-runtime-reference" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#javascript-sdk-runtime-reference"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>JavaScript SDK runtime reference</span></h1><!--]--><!----> <blockquote><p><strong>Building a Reachy Mini JS app?</strong> The single source of truth is <a href="../../../ts/APP_CREATION_GUIDE"><code>ts/APP_CREATION_GUIDE.md</code></a> at
the repo root. It covers scaffolding, <code>public/icon.svg</code>, the host
shell, <code>sdk: static</code> deploy, <code>mountHost()</code> / <code>connectToHost()</code> API, local dev, FAQ, and the host
↔ embed contract. <strong>Pin the SDK to <code>@pollen-robotics/reachy-mini-sdk@1.8.0</code></strong> (the stable release
validated against the host shell + daemon).</p> <p><strong>This file</strong> is the runtime API surface of the <code>ReachyMini</code> class
you receive from <code>handle.reachy</code> once <code>connectToHost()</code> resolves:
methods, events, properties, state machine, and the daemon-side
recorded-move playback API. Bookmark it after you’ve shipped a
first app from the guide.</p></blockquote> <p>Reachy Mini ships a browser SDK that drives a robot over WebRTC.
The npm package <code>@pollen-robotics/reachy-mini-sdk</code> exposes:</p> <ul><li>The <code>ReachyMini</code> class (the SDK runtime documented below).</li> <li>The host shell + embed adapter under the <code>./host*</code> subpath
exports (<code>./host</code>, <code>./host/auto</code>, <code>./host/embed</code>, <code>./host/protocol</code>).
See <a href="../../../ts/APP_CREATION_GUIDE"><code>ts/APP_CREATION_GUIDE.md</code></a> for the integration recipe.</li></ul> <!--[1--><h2 class="relative group"><a id="architecture" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#architecture"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Architecture</span></h2><!--]--><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class=" "><!---->┌─────────────────────────────────┐
│ Browser │
│ (your app + reachy-mini-sdk<span class="hljs-selector-class">.js</span>)│
└───────┬────────────┬────────────┘
│ SSE/HTTP │ WebRTC (peer-<span class="hljs-selector-tag">to</span>-peer)
│ signaling │ <span class="hljs-selector-tag">video</span> + <span class="hljs-selector-tag">audio</span> + data
┌───────▼──────┐ │
│ Signaling │ │
│ Server │ │
│ (HF Space) │ │
└───────┬──────┘ │
│ │
┌───────▼────────────▼────────────┐
│ Robot │
│ GStreamer WebRTC daemon │
│ camera · mic · motors │
└─────────────────────────────────┘<!----></pre></div><!----> <ol><li>Your app is a static HTML/JS page hosted on Hugging Face Spaces.</li> <li>The SDK handles authentication, signaling, and WebRTC negotiation.</li> <li>The signaling server relays SDP offers/answers and ICE candidates
and validates Hugging Face OAuth tokens.</li> <li>Once the WebRTC connection is established, video, audio, and
commands flow peer-to-peer; the signaling server is no longer in
the path.</li></ol> <p>When you use the host shell (<code>mountHost()</code> + <code>connectToHost()</code>,
documented in <a href="../../../ts/APP_CREATION_GUIDE"><code>ts/APP_CREATION_GUIDE.md</code></a>),
the steps below are handled for you. The class-level API documented
here is what you use <strong>after</strong> <code>connectToHost()</code> resolves, or what
you call directly if you opted out of the host shell.</p> <!--[1--><h2 class="relative group"><a id="api-reference" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#api-reference"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>API Reference</span></h2><!--]--><!----> <!--[2--><h3 class="relative group"><a id="constructor" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#constructor"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Constructor</span></h3><!--]--><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-js "><!----><span class="hljs-keyword">new</span> <span class="hljs-title class_">ReachyMini</span>({
<span class="hljs-attr">signalingUrl</span>: <span class="hljs-string">&quot;https://pollen-robotics-reachy-mini-central.hf.space&quot;</span>, <span class="hljs-comment">// default</span>
<span class="hljs-attr">clientId</span>: <span class="hljs-string">&quot;my-app&quot;</span>, <span class="hljs-comment">// optional — OAuth client / app id</span>
<span class="hljs-attr">appName</span>: <span class="hljs-string">&quot;My App&quot;</span>, <span class="hljs-comment">// optional — label advertised to the robot</span>
<span class="hljs-attr">videoJitterBufferTargetMs</span>: <span class="hljs-number">0</span>, <span class="hljs-comment">// optional — 0 = render ASAP (teleop); omit for default browser buffering</span>
<span class="hljs-attr">autoStartFromUrl</span>: <span class="hljs-literal">false</span>, <span class="hljs-comment">// optional — auto startSession() when the URL carries a robot_peer_id hint</span>
<span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> `enableMicrophone` is deprecated and ignored. The SDK never grabs</span>
<span class="hljs-comment">// the user&#x27;s mic; it always wires a silent placeholder audio sender you</span>
<span class="hljs-comment">// can replaceTrack() with your own audio (TTS, files, or the user&#x27;s mic).</span>
})<!----></pre></div><!----> <!--[2--><h3 class="relative group"><a id="state-machine" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#state-machine"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>State Machine</span></h3><!--]--><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class=" "><!----><span class="hljs-string">&#x27;disconnected&#x27;</span> ──<span class="hljs-function"><span class="hljs-title">connect</span>()──▸ <span class="hljs-string">&#x27;connected&#x27;</span> ──<span class="hljs-title">startSession</span>()──▸ <span class="hljs-string">&#x27;streaming&#x27;</span>
<span class="hljs-title">disconnect</span>() ▴ <span class="hljs-title">stopSession</span>()</span>
└─────────────────────────────┘<!----></pre></div><!----> <blockquote><p><strong>One-shot bring-up.</strong> <code>autoConnect(options?)</code> runs the whole
auth → <code>connect()</code> → robot pick → <code>startSession()</code><code>ensureAwake()</code> chain and resolves to <code>{ robotId, robotName, isEmbedded }</code>. It
auto-picks when exactly one robot is free, otherwise calls your <code>options.pickRobot(robots)</code> callback. This is the recommended entry
point when you’re <strong>not</strong> using the host shell.</p></blockquote> <!--[2--><h3 class="relative group"><a id="properties-read-only" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#properties-read-only"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Properties (read-only)</span></h3><!--]--><!----> <table><thead><tr><th align="left">Property</th><th align="left">Type</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><code>state</code></td><td align="left"><code>string</code></td><td align="left"><code>"disconnected"</code>, <code>"connected"</code>, or <code>"streaming"</code></td></tr><tr><td align="left"><code>robots</code></td><td align="left"><code>Array</code></td><td align="left">Available robots: <code>[{ id, meta: { name } }]</code></td></tr><tr><td align="left"><code>robotState</code></td><td align="left"><code>Object</code></td><td align="left">Latest <code>state</code> event detail — <code>{ head: number[16], antennas: [rRad, lRad], head_joint_positions: number[7], antennas_joint_positions: [rRad, lRad], body_yaw, motor_mode, is_move_running }</code> (wire shape). <code>head_joint_positions</code> is per-motor radians: body yaw at <code>[0]</code>, the 6 Stewart-platform neck motors at <code>[1..6]</code>; <code>antennas_joint_positions</code> is <code>[right, left]</code> in radians. Both fields appear only once the daemon emits them.</td></tr><tr><td align="left"><code>username</code></td><td align="left"><code>string\|null</code></td><td align="left">HF username after <code>authenticate()</code></td></tr><tr><td align="left"><code>isAuthenticated</code></td><td align="left"><code>boolean</code></td><td align="left">True if a valid HF token is available</td></tr><tr><td align="left"><code>micSupported</code></td><td align="left"><code>boolean</code></td><td align="left">True if robot offers bidirectional audio</td></tr><tr><td align="left"><code>micMuted</code></td><td align="left"><code>boolean</code></td><td align="left">Your microphone mute state</td></tr><tr><td align="left"><code>audioMuted</code></td><td align="left"><code>boolean</code></td><td align="left">Robot speaker mute state (local only)</td></tr><tr><td align="left"><code>preselectedRobotId</code></td><td align="left"><code>string\|null</code></td><td align="left">Robot id read from <code>?robot_peer_id=</code> / <code>#robot_peer_id=</code> (set by the host iframe)</td></tr><tr><td align="left"><code>isEmbedded</code></td><td align="left"><code>boolean</code></td><td align="left"><code>true</code> iff <code>preselectedRobotId !== null</code> — UX branching helper (embed vs standalone)</td></tr></tbody></table> <!--[2--><h3 class="relative group"><a id="methods" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#methods"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Methods</span></h3><!--]--><!----> <table><thead><tr><th align="left">Method</th><th align="left">Returns</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><code>authenticate()</code></td><td align="left"><code>Promise&lt;boolean></code></td><td align="left">Check for existing HF OAuth token</td></tr><tr><td align="left"><code>login()</code></td><td align="left"></td><td align="left">Redirect to HF login page</td></tr><tr><td align="left"><code>connect()</code></td><td align="left"><code>Promise</code></td><td align="left">Open SSE connection, receive robot list</td></tr><tr><td align="left"><code>autoConnect(opts?)</code></td><td align="left"><code>Promise&lt;{robotId, robotName, isEmbedded}></code></td><td align="left">One-shot bring-up: auth → connect → pick robot → session → wake. Auto-picks a single free robot, else calls <code>opts.pickRobot(robots)</code></td></tr><tr><td align="left"><code>startSession(robotId)</code></td><td align="left"><code>Promise</code></td><td align="left">Negotiate WebRTC, resolves when video + data ready</td></tr><tr><td align="left"><code>stopSession()</code></td><td align="left"><code>Promise</code></td><td align="left">End session, back to <code>connected</code></td></tr><tr><td align="left"><code>disconnect()</code></td><td align="left"></td><td align="left">Close signaling (keeps auth)</td></tr><tr><td align="left"><code>logout()</code></td><td align="left"></td><td align="left">Clear HF credentials</td></tr><tr><td align="left"><code>attachVideo(videoEl)</code></td><td align="left"><code>() => void</code></td><td align="left">Bind video stream to element; returns cleanup function</td></tr><tr><td align="left"><code>setTarget({ head?, antennas?, body_yaw? })</code></td><td align="left"><code>boolean</code></td><td align="left">Atomic raw-units update — <code>head</code> is <code>number[16]</code> (flat 4×4), <code>antennas</code> is <code>[rRad, lRad]</code>, <code>body_yaw</code> is radians</td></tr><tr><td align="left"><code>gotoTarget({ head?, antennas?, body_yaw?, duration })</code></td><td align="left"><code>boolean</code></td><td align="left">Smooth daemon-side interpolation to a target pose over <code>duration</code> seconds (same wire units as <code>setTarget</code>). Throws <code>TypeError</code> on invalid input</td></tr><tr><td align="left"><code>setHeadRpyDeg(roll, pitch, yaw)</code></td><td align="left"><code>boolean</code></td><td align="left">Set head orientation in degrees (wraps <code>setTarget</code>)</td></tr><tr><td align="left"><code>setAntennasDeg(right, left)</code></td><td align="left"><code>boolean</code></td><td align="left">Set antenna positions in degrees (wraps <code>setTarget</code>)</td></tr><tr><td align="left"><code>setBodyYawDeg(yaw)</code></td><td align="left"><code>boolean</code></td><td align="left">Set body yaw in degrees (wraps <code>setTarget</code>)</td></tr><tr><td align="left"><code>startHeadTracking(weight?)</code> / <code>stopHeadTracking()</code></td><td align="left"><code>boolean</code></td><td align="left">Enable / disable daemon-side visual head tracking. <code>weight</code> in <code>[0,1]</code> blends tracking with app motion (default <code>1.0</code>)</td></tr><tr><td align="left"><code>getTrackedFace()</code></td><td align="left"><code>Promise&lt;FaceTarget\|null></code></td><td align="left">Latest face seen by head tracking — <code>{ detected, x, y, roll }</code></td></tr><tr><td align="left"><code>setMotorMode(mode)</code></td><td align="left"><code>boolean</code></td><td align="left"><code>"enabled"</code> (position control), <code>"disabled"</code> (limp), or <code>"gravity_compensation"</code> (float by hand)</td></tr><tr><td align="left"><code>setMotorTorque(on, ids?)</code></td><td align="left"><code>boolean</code></td><td align="left">Toggle torque; per-motor when <code>ids</code> is given, else global</td></tr><tr><td align="left"><code>wakeUp(opts?)</code> / <code>gotoSleep(opts?)</code></td><td align="left"><code>Promise&lt;void></code></td><td align="left">Play the wake-up / sleep trajectory; resolves on daemon completion (rejects after <code>opts.timeoutMs</code>, default 8000). <code>wakeUp</code> enables motors first</td></tr><tr><td align="left"><code>isAwake()</code></td><td align="left"><code>boolean</code></td><td align="left">Awake state derived from the cached <code>motor_mode</code> (<code>gravity_compensation</code> counts as awake)</td></tr><tr><td align="left"><code>ensureAwake(timeoutMs?)</code></td><td align="left"><code>Promise&lt;boolean></code></td><td align="left">Idempotent bring-up to position control: awaits the wake trajectory when asleep, flips <code>gravity_compensation</code> back to <code>enabled</code> (no emote), no-op when already there. Never rejects</td></tr><tr><td align="left"><code>playSound(filename)</code></td><td align="left"><code>boolean</code></td><td align="left">Play a sound file on the robot</td></tr><tr><td align="left"><code>clearIncomingAudio()</code></td><td align="left"><code>boolean</code></td><td align="left">Drop audio queued for the robot speaker (barge-in)</td></tr><tr><td align="left"><code>sendRaw(data)</code></td><td align="left"><code>boolean</code></td><td align="left">Send arbitrary JSON via data channel</td></tr><tr><td align="left"><code>requestState()</code></td><td align="left"><code>boolean</code></td><td align="left">Request a state snapshot</td></tr><tr><td align="left"><code>setAudioMuted(muted)</code></td><td align="left"></td><td align="left">Mute/unmute robot speaker (local)</td></tr><tr><td align="left"><code>setMicMuted(muted)</code></td><td align="left"></td><td align="left">Mute/unmute your microphone</td></tr><tr><td align="left"><code>getVolume()</code> / <code>setVolume(v)</code></td><td align="left"><code>Promise&lt;number\|null></code></td><td align="left">Read / set speaker volume (integer <code>0..100</code>, clamped); resolves the applied value or <code>null</code> if unavailable</td></tr><tr><td align="left"><code>getMicrophoneVolume()</code> / <code>setMicrophoneVolume(v)</code></td><td align="left"><code>Promise&lt;number\|null></code></td><td align="left">Read / set microphone gain (integer <code>0..100</code>, clamped)</td></tr><tr><td align="left"><code>getVersion()</code></td><td align="left"><code>Promise&lt;string\|null></code></td><td align="left">Daemon version string</td></tr><tr><td align="left"><code>getHardwareId()</code></td><td align="left"><code>Promise&lt;string\|null></code></td><td align="left">Hardware ID (USB serial); <code>null</code> on developer machines</td></tr><tr><td align="left"><code>applyAudioConfig(config, opts?)</code></td><td align="left"><code>Promise&lt;boolean></code></td><td align="left">Write a batch of XVF3800 audio-board parameters (<code>[{ name, values }]</code>); <code>opts.verify</code> reads them back (default <code>true</code>). Wireless only</td></tr><tr><td align="left"><code>readAudioParameter(name)</code></td><td align="left"><code>Promise&lt;number[]\|null></code></td><td align="left">Read one XVF3800 parameter by name</td></tr><tr><td align="left"><code>subscribeLogs({ onLine, onError? })</code></td><td align="left"><code>() => void</code></td><td align="left">Stream the daemon’s <code>journalctl</code> logs over the data channel; returns an unsubscribe fn</td></tr><tr><td align="left"><code>playRecordedMove(name, opts?)</code></td><td align="left"><code>boolean</code></td><td align="left">Play a named move (motion + bundled sound) from a HF dataset, daemon-side. Fire-and-forget (resolves on send, not on playback end). <code>opts.dataset</code> defaults to the pre-downloaded emotions library; <code>opts.initialGotoDuration</code> sets the lead-in goto</td></tr><tr><td align="left"><code>playMove(motion, opts?)</code></td><td align="left"><code>Promise&lt;{finished?, cancelled?, error?, has_audio?}></code></td><td align="left">Upload + play a recorded move (optionally with audio) on the daemon’s local clock; resolves when playback ends — see <a href="#daemon-side-recorded-move-playback">Daemon-side recorded-move playback</a></td></tr><tr><td align="left"><code>cancelMove()</code></td><td align="left"><code>boolean</code></td><td align="left">Cancel an in-flight <code>playMove</code></td></tr><tr><td align="left"><code>uploadAudio(blob, opts?)</code></td><td align="left"><code>Promise&lt;string></code></td><td align="left">Upload a standalone audio slot, returns <code>uploadId</code> — pair with <code>playUploadedAudio</code> for record-time sync</td></tr><tr><td align="left"><code>playUploadedAudio(uploadId, opts?)</code></td><td align="left"><code>Promise&lt;{started: true, ...}></code></td><td align="left">Trigger daemon-side standalone audio playback; resolves on the daemon’s <code>started</code> broadcast (use as a sync anchor)</td></tr><tr><td align="left"><code>cancelAudio()</code></td><td align="left"><code>boolean</code></td><td align="left">Cancel an in-flight <code>playUploadedAudio</code></td></tr><tr><td align="left"><code>subscribePose()</code> / <code>unsubscribePose()</code></td><td align="left"><code>boolean</code></td><td align="left">Opt into a ~30 Hz daemon-pushed pose stream (unreliable/unordered data channel) that fires <code>state</code> events, instead of the 500 ms <code>requestState</code> poll. Refcounted — pair each subscribe with one unsubscribe. No-op on daemons without the pose channel</td></tr><tr><td align="left"><code>getFirstWakeUp()</code> / <code>setFirstWakeUp(done)</code></td><td align="left"><code>Promise&lt;boolean\|null></code></td><td align="left">Read / persist the robot-wide “first wake-up wizard completed” flag. <code>null</code> = channel closed or daemon predates the command (fail-open: skip the wizard)</td></tr><tr><td align="left"><code>getRobotName()</code> / <code>setRobotName(name)</code></td><td align="left"><code>Promise&lt;string\|null></code></td><td align="left">Read / persist the robot display name. Applied live by the daemon (status + central relay + mDNS), no restart needed. Resolves the stored name, or <code>null</code> on error / unsupported</td></tr><tr><td align="left"><code>signOut()</code></td><td align="left"><code>Promise&lt;boolean\|null></code></td><td align="left">Sign the robot out of Hugging Face (daemon deletes its stored HF token, de-registering it from central). Reaches the robot remotely over the data channel. The session may drop right after the ack — expected</td></tr></tbody></table> <blockquote><p><strong><code>setTarget</code> head-vs-body coupling.</strong> The <code>head</code> matrix is in the
world frame. Sending <code>setTarget({ body_yaw })</code> alone rotates the
body <em>but not the head’s commanded world yaw</em> — the head’s gaze
stays fixed in world frame, so visually it appears to counter-rotate
as the body turns. For tank-style “head follows body”, add the body
yaw delta to the head RPY’s yaw and ship <code>head</code> + <code>body_yaw</code> in the
same <code>setTarget</code> call. The baseline for the head yaw must be the
last <em>commanded</em> value you tracked yourself, not <code>state.head</code> from
the telemetry event — telemetry lags one WebRTC RTT and cumulative
deltas computed against it stall under rapid input.</p></blockquote> <!--[2--><h3 class="relative group"><a id="events" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#events"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Events</span></h3><!--]--><!----> <p>Use <code>robot.addEventListener(name, handler)</code> — the SDK extends <code>EventTarget</code>.</p> <table><thead><tr><th align="left">Event</th><th align="left">Detail</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left"><code>connected</code></td><td align="left"><code>{ peerId }</code></td><td align="left">Signaling connection established</td></tr><tr><td align="left"><code>disconnected</code></td><td align="left"><code>{ reason }</code></td><td align="left">Signaling connection lost</td></tr><tr><td align="left"><code>robotsChanged</code></td><td align="left"><code>{ robots }</code></td><td align="left">Robot list updated</td></tr><tr><td align="left"><code>streaming</code></td><td align="left"><code>{ sessionId, robotId }</code></td><td align="left">WebRTC session active</td></tr><tr><td align="left"><code>sessionStopped</code></td><td align="left"><code>{ reason }</code></td><td align="left">Session ended</td></tr><tr><td align="left"><code>state</code></td><td align="left">Same shape as the <code>robotState</code> property (see above)</td><td align="left">Robot state update (~500 ms polled, or ~30 Hz when subscribed via <code>subscribePose()</code>; wire shape)</td></tr><tr><td align="left"><code>videoTrack</code></td><td align="left"><code>{ track, stream }</code></td><td align="left">Video track available</td></tr><tr><td align="left"><code>micSupported</code></td><td align="left"><code>{ supported }</code></td><td align="left">Bidirectional audio availability</td></tr><tr><td align="left"><code>error</code></td><td align="left"><code>{ source, error }</code></td><td align="left">Error from <code>signaling</code>, <code>webrtc</code>, or <code>robot</code></td></tr><tr><td align="left"><code>sessionRejected</code></td><td align="left"><code>{ reason, activeApp }</code></td><td align="left">The robot refused the session (e.g. busy with another app)</td></tr><tr><td align="left"><code>iceStateChange</code></td><td align="left"><code>{ state }</code></td><td align="left">Granular ICE transition (<code>RTCIceConnectionState</code>); a transient <code>disconnected</code> is debounced before it escalates to <code>error</code></td></tr><tr><td align="left"><code>networkOnline</code> / <code>networkOffline</code></td><td align="left"><code>{}</code></td><td align="left">Browser connectivity regained / lost</td></tr><tr><td align="left"><code>networkChange</code></td><td align="left"><code>{ effectiveType?, downlink?, rtt?, saveData? }</code></td><td align="left">Transport swap (Wi-Fi ↔ cellular) via the NetworkInformation API; best-effort, Chromium-only</td></tr></tbody></table> <!--[2--><h3 class="relative group"><a id="math-utilities" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#math-utilities"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Math Utilities</span></h3><!--]--><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-js "><!----><span class="hljs-keyword">import</span> { rpyToMatrix, matrixToRpy, degToRad, radToDeg } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@pollen-robotics/reachy-mini-sdk&quot;</span>;
<span class="hljs-title function_">rpyToMatrix</span>(roll, pitch, yaw) <span class="hljs-comment">// degrees → 4×4 rotation matrix (ZYX)</span>
<span class="hljs-title function_">matrixToRpy</span>(matrix) <span class="hljs-comment">// 4×4 matrix → { roll, pitch, yaw } in degrees</span><!----></pre></div><!----> <!--[2--><h3 class="relative group"><a id="advanced-json-rpc--daemon-admin" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#advanced-json-rpc--daemon-admin"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Advanced: JSON-RPC &amp; daemon admin</span></h3><!--]--><!----> <p>Beyond the typed surface above, the runtime object exposes lower-level hooks (not part of the host-shell typed handle — reach for these only when you need them):</p> <ul><li><code>rpcCall(method, params?, { timeoutMs? })</code> — send a JSON-RPC request over the data channel and await the correlated result (e.g. app-defined methods).</li> <li><code>onNotification(method, cb)</code> — subscribe to one-way JSON-RPC notifications pushed by the robot/app (e.g. <code>conversation.turn</code>); returns an unsubscribe fn.</li> <li><code>startDaemonUpdate({ preRelease?, onProgress? })</code> — trigger a PyPI update of the daemon. It restarts on success (which tears the session down), so treat a successful reconnect as the “done” signal; <code>onProgress</code> fires with <code>status: "failed"</code> if the install errors first.</li></ul> <!--[2--><h3 class="relative group"><a id="debug-logging" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#debug-logging"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Debug logging</span></h3><!--]--><!----> <p>Every SDK log line is prefixed <code>[reachy:&lt;ns>]</code> (<code>reachy:sdk</code>, <code>reachy:session</code> for auto-reconnect, <code>reachy:embed</code> / <code>reachy:host</code> for the host shell), so a devtools console filter on <code>reachy:</code> isolates the whole stack. The default level is <code>info</code>: a handful of lifecycle lines per session (boot phases, wake/sleep steps, reconnects). To see per-message traffic (every command, reply, and SSE event):</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-js "><!----><span class="hljs-comment">// From the devtools console (persists across reloads):</span>
<span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">setItem</span>(<span class="hljs-string">&quot;reachy-log&quot;</span>, <span class="hljs-string">&quot;debug&quot;</span>); location.<span class="hljs-title function_">reload</span>();
<span class="hljs-comment">// Or from code:</span>
<span class="hljs-keyword">import</span> { setLogLevel } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@pollen-robotics/reachy-mini-sdk&quot;</span>;
<span class="hljs-title function_">setLogLevel</span>(<span class="hljs-string">&quot;debug&quot;</span>); <span class="hljs-comment">// &quot;debug&quot; | &quot;info&quot; | &quot;warn&quot; | &quot;error&quot; | &quot;silent&quot;</span><!----></pre></div><!----> <p>Debug lines use <code>console.debug</code>, so also enable the “Verbose” level in the devtools console filter to see them.</p> <!--[1--><h2 class="relative group"><a id="daemon-side-recorded-move-playback" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#daemon-side-recorded-move-playback"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Daemon-side recorded-move playback</span></h2><!--]--><!----> <p>Long recorded moves (and any move with audio) should play <strong>server-side on the daemon’s local clock</strong>, not by streaming <code>set_target</code> frames from the browser. The browser uploads the move once over the WebRTC data channel and the daemon ticks the inner loop at the requested frequency — no per-frame round-trip, smooth on wireless robots. When audio is attached the daemon plays it on the same GStreamer pipeline, so motion and audio share a single clock (no cross-network drift).</p> <!--[2--><h3 class="relative group"><a id="combined-motion--audio" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#combined-motion--audio"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Combined motion + audio</span></h3><!--]--><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-js "><!----><span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> robot.<span class="hljs-title function_">playMove</span>(motion, {
audioBlob, <span class="hljs-comment">// optional, 16 kHz mono PCM WAV</span>
<span class="hljs-attr">audioLeadMs</span>: -<span class="hljs-number">100</span>, <span class="hljs-comment">// system-wide default</span>
<span class="hljs-attr">description</span>: <span class="hljs-string">&quot;happy wave&quot;</span>,
<span class="hljs-attr">onProgress</span>: <span class="hljs-function">(<span class="hljs-params">p</span>) =&gt;</span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(p.<span class="hljs-property">phase</span>, p.<span class="hljs-property">sent</span>, p.<span class="hljs-property">total</span>),
<span class="hljs-attr">onStarted</span>: <span class="hljs-function">(<span class="hljs-params">{ duration_s, has_audio }</span>) =&gt;</span> { <span class="hljs-comment">/* sync anchor */</span> },
});
<span class="hljs-comment">// result is { finished: true } | { cancelled: true } | { error: &quot;...&quot; }</span>
<span class="hljs-comment">// Cancel at any time from another code path:</span>
robot.<span class="hljs-title function_">cancelMove</span>();<!----></pre></div><!----> <p><code>motion</code> is the shape the Python <code>RecordedMove</code> parser expects:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-js "><!---->{ <span class="hljs-attr">time</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">0.01</span>, <span class="hljs-number">0.02</span>, …], <span class="hljs-attr">set_target_data</span>: [{ head, antennas, body_yaw }, …] }<!----></pre></div><!----> <p><code>audioLeadMs</code> shifts audio relative to motion at the daemon:</p> <ul><li><strong>Positive</strong> — audio fires N ms BEFORE motion (compensates motor pickup).</li> <li><strong>Negative</strong> — motion fires N ms BEFORE audio (compensates GStreamer playbin warmup).</li> <li><strong>Default <code>-100</code></strong> is the empirical system-wide constant (combined motor + pipeline). Tune only after measuring.</li></ul> <p>The encoded wire form defaults to <code>gzip+base64</code> (typically ~3× smaller for recorded-move JSON). Falls back to plain JSON if the browser lacks <code>CompressionStream</code>.</p> <!--[2--><h3 class="relative group"><a id="record-time-audio-sync-anchor" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#record-time-audio-sync-anchor"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Record-time audio (sync anchor)</span></h3><!--]--><!----> <p>For recording flows that want the SAME audio pipeline at capture AND replay (so pipeline latency cancels out and one <code>audioLeadMs</code> works for all recordings):</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-js "><!----><span class="hljs-comment">// 1. During the countdown — upload the source audio.</span>
<span class="hljs-keyword">const</span> audioId = <span class="hljs-keyword">await</span> robot.<span class="hljs-title function_">uploadAudio</span>(audioBlob, { <span class="hljs-attr">description</span>: <span class="hljs-string">&quot;song&quot;</span> });
<span class="hljs-comment">// 2. At the GO! moment — kick off daemon-side playback, await the</span>
<span class="hljs-comment">// started broadcast, then start motion capture.</span>
<span class="hljs-keyword">await</span> robot.<span class="hljs-title function_">playUploadedAudio</span>(audioId);
<span class="hljs-keyword">const</span> captureT0 = performance.<span class="hljs-title function_">now</span>();
<span class="hljs-title function_">startMyMotionCapture</span>();
<span class="hljs-comment">// 3. On stop / cancel / restart — stop the audio.</span>
robot.<span class="hljs-title function_">cancelAudio</span>();<!----></pre></div><!----> <p>The daemon does NOT emit a <code>finished</code> event for standalone audio; callers know the duration from the WAV header and call <code>cancelAudio()</code> when done.</p> <!--[2--><h3 class="relative group"><a id="audio-format" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#audio-format"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Audio format</span></h3><!--]--><!----> <p>Audio must be canonical <strong>16 kHz mono 16-bit PCM WAV</strong>. Apps are responsible for normalizing before upload — the daemon does not transcode. Format mismatch is a frequent cause of “audio is silent / wrong speed” on inherited datasets.</p> <!--[2--><h3 class="relative group"><a id="backpressure--cancellation" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#backpressure--cancellation"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Backpressure &amp; cancellation</span></h3><!--]--><!----> <p><code>playMove</code> and <code>uploadAudio</code> pace chunk sends on the data channel’s <code>bufferedAmount</code> so multi-megabyte uploads (a 3-min song’s WAV is ~6 MB base64) don’t degrade other channels on the same peer connection. There’s no separate <code>pause</code> — to stop a long upload mid-way, close the session.</p> <!--[1--><h2 class="relative group"><a id="live-pose-stream-30-hz" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#live-pose-stream-30-hz"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Live pose stream (30 Hz)</span></h2><!--]--><!----> <p>By default the SDK refreshes <code>robotState</code> by polling <code>requestState()</code> every
500 ms. For anything that mirrors the robot in real time (a 3D view, a
“wait for the move to finish” watcher), opt into the daemon’s <strong>pose
stream</strong> instead: it <em>pushes</em> the state at ~30 Hz over a dedicated
unreliable/unordered data channel, firing the same <code>state</code> events.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-js "><!---->robot.<span class="hljs-title function_">subscribePose</span>(); <span class="hljs-comment">// start the push stream</span>
robot.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">&quot;state&quot;</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
<span class="hljs-comment">// e.detail.head_joint_positions → per-motor radians (body yaw + 6 neck)</span>
<span class="hljs-title function_">render</span>(e.<span class="hljs-property">detail</span>);
});
<span class="hljs-comment">// Later, when this consumer no longer needs the stream:</span>
robot.<span class="hljs-title function_">unsubscribePose</span>();<!----></pre></div><!----> <p>The subscription is <strong>refcounted</strong>: multiple consumers share one daemon-side
stream, so pair every <code>subscribePose()</code> with exactly one <code>unsubscribePose()</code> — the daemon only stops pushing once the last consumer releases. Frames that
arrive out of order are dropped (the channel is unordered). Against an older
daemon that has no pose channel this is a no-op; fall back to <code>requestState()</code> polling there.</p> <!--[1--><h2 class="relative group"><a id="robot-onboarding--management" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#robot-onboarding--management"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Robot onboarding &amp; management</span></h2><!--]--><!----> <p>These daemon-side commands power first-run setup and remote administration.
Each resolves <code>null</code> when the data channel isn’t open or the daemon predates
the command, so <strong>fail-open</strong>: treat <code>null</code> as “unsupported” and skip the
gated UI.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-js "><!----><span class="hljs-comment">// First wake-up wizard (robot-wide, persisted on the robot).</span>
<span class="hljs-keyword">if</span> ((<span class="hljs-keyword">await</span> robot.<span class="hljs-title function_">getFirstWakeUp</span>()) === <span class="hljs-literal">false</span>) {
<span class="hljs-keyword">await</span> <span class="hljs-title function_">runWakeUpWizard</span>();
<span class="hljs-keyword">await</span> robot.<span class="hljs-title function_">setFirstWakeUp</span>(<span class="hljs-literal">true</span>);
}
<span class="hljs-comment">// Robot display name — applied live (status + central relay + mDNS), no restart.</span>
<span class="hljs-keyword">const</span> name = <span class="hljs-keyword">await</span> robot.<span class="hljs-title function_">getRobotName</span>();
<span class="hljs-keyword">await</span> robot.<span class="hljs-title function_">setRobotName</span>(<span class="hljs-string">&quot;Marvin&quot;</span>);
<span class="hljs-comment">// Sign the robot out of Hugging Face (deletes its stored token; the robot</span>
<span class="hljs-comment">// disappears from its owner&#x27;s list until set up again). The session may</span>
<span class="hljs-comment">// tear down right after the ack — that&#x27;s expected.</span>
<span class="hljs-keyword">await</span> robot.<span class="hljs-title function_">signOut</span>();<!----></pre></div><!----> <!--[1--><h2 class="relative group"><a id="security" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#security"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Security</span></h2><!--]--><!----> <ul><li>Authentication goes through Hugging Face OAuth — only users logged in to HF can access the signaling server.</li> <li>By default, you can only connect to robots registered under your own HF account.</li> <li>WebRTC connections are encrypted (DTLS/SRTP).</li></ul> <!--[1--><h2 class="relative group"><a id="prerequisites" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#prerequisites"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Prerequisites</span></h2><!--]--><!----> <ul><li>Your robot must be running the wireless firmware and connected to the central signaling server.</li> <li>The robot must have a valid Hugging Face token configured (see <a href="../platforms/reachy_mini/usage">Usage</a>).</li> <li>Currently supported on <strong>wireless versions</strong> only.</li></ul> <!--[1--><h2 class="relative group"><a id="working-examples" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#working-examples"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Working examples</span></h2><!--]--><!----> <p>The three reference apps maintained alongside the SDK are the canonical worked examples. They all use the host shell pattern and the current SDK pin:</p> <ul><li><a href="https://huggingface.co/spaces/pollen-robotics/reachy_mini_minimal_conversation" rel="nofollow"><code>pollen-robotics/reachy_mini_minimal_conversation</code></a> — vanilla TS + Vite.</li> <li><a href="https://huggingface.co/spaces/pollen-robotics/reachy_mini_emotions" rel="nofollow"><code>pollen-robotics/reachy_mini_emotions</code></a> — React 19 + MUI 7 + Vite.</li> <li><a href="https://huggingface.co/spaces/pollen-robotics/reachy_mini_telepresence" rel="nofollow"><code>pollen-robotics/reachy_mini_telepresence</code></a> — React 19 + MUI 7 + Vite with camera + media streams.</li></ul> <p>Clone the closest one and trim. See <a href="../../../ts/APP_CREATION_GUIDE"><code>ts/APP_CREATION_GUIDE.md</code></a> for the step-by-step.</p> <a class="!text-gray-400 !no-underline text-sm flex items-center not-prose mt-4" href="https://github.com/pollen-robotics/reachy_mini/blob/main/docs/source/SDK/javascript-sdk.md" target="_blank"><svg class="mr-1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M31,16l-7,7l-1.41-1.41L28.17,16l-5.58-5.59L24,9l7,7z"></path><path d="M1,16l7-7l1.41,1.41L3.83,16l5.58,5.59L8,23l-7-7z"></path><path d="M12.419,25.484L17.639,6.552l1.932,0.518L14.351,26.002z"></path></svg><!----> <span><span class="underline">Update</span> on GitHub</span></a><!----> <p></p><!--]--><!----><!--]--><!--]--><!--]--> <!--[-1--><!--]--><!--]-->
<script>
{
__sveltekit_1670ng5 = {
base: "/docs/reachy_mini/pr_1321/en",
assets: "/docs/reachy_mini/pr_1321/en"
};
const element = document.currentScript.parentElement;
Promise.all([
import("/docs/reachy_mini/pr_1321/en/_app/immutable/entry/start.DEyGTiAz.js"),
import("/docs/reachy_mini/pr_1321/en/_app/immutable/entry/app.Cr_rB8lm.js")
]).then(([kit, app]) => {
kit.start(app, element, {
node_ids: [0, 9],
data: [null,null],
form: null,
error: null
});
});
}
</script>

Xet Storage Details

Size:
80.7 kB
·
Xet hash:
67165af4e96b1ed90d4ef8a2cfd72b643cea212ccc8f06c75dffcc87c4b7a4cf

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.