Buckets:
| <meta charset="utf-8" /><meta name="hf:doc:metadata" content="{"title":"JavaScript SDK runtime reference","local":"javascript-sdk-runtime-reference","sections":[{"title":"Architecture","local":"architecture","sections":[],"depth":2},{"title":"API Reference","local":"api-reference","sections":[{"title":"Constructor","local":"constructor","sections":[],"depth":3},{"title":"State Machine","local":"state-machine","sections":[],"depth":3},{"title":"Properties (read-only)","local":"properties-read-only","sections":[],"depth":3},{"title":"Methods","local":"methods","sections":[],"depth":3},{"title":"Events","local":"events","sections":[],"depth":3},{"title":"Math Utilities","local":"math-utilities","sections":[],"depth":3},{"title":"Advanced: JSON-RPC & daemon admin","local":"advanced-json-rpc--daemon-admin","sections":[],"depth":3},{"title":"Debug logging","local":"debug-logging","sections":[],"depth":3}],"depth":2},{"title":"Daemon-side recorded-move playback","local":"daemon-side-recorded-move-playback","sections":[{"title":"Combined motion + audio","local":"combined-motion--audio","sections":[],"depth":3},{"title":"Record-time audio (sync anchor)","local":"record-time-audio-sync-anchor","sections":[],"depth":3},{"title":"Audio format","local":"audio-format","sections":[],"depth":3},{"title":"Backpressure & cancellation","local":"backpressure--cancellation","sections":[],"depth":3}],"depth":2},{"title":"Live pose stream (30 Hz)","local":"live-pose-stream-30-hz","sections":[],"depth":2},{"title":"Robot onboarding & management","local":"robot-onboarding--management","sections":[],"depth":2},{"title":"Security","local":"security","sections":[],"depth":2},{"title":"Prerequisites","local":"prerequisites","sections":[],"depth":2},{"title":"Working examples","local":"working-examples","sections":[],"depth":2}],"depth":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="{"title":"JavaScript SDK runtime reference","local":"javascript-sdk-runtime-reference","sections":[{"title":"Architecture","local":"architecture","sections":[],"depth":2},{"title":"API Reference","local":"api-reference","sections":[{"title":"Constructor","local":"constructor","sections":[],"depth":3},{"title":"State Machine","local":"state-machine","sections":[],"depth":3},{"title":"Properties (read-only)","local":"properties-read-only","sections":[],"depth":3},{"title":"Methods","local":"methods","sections":[],"depth":3},{"title":"Events","local":"events","sections":[],"depth":3},{"title":"Math Utilities","local":"math-utilities","sections":[],"depth":3},{"title":"Advanced: JSON-RPC & daemon admin","local":"advanced-json-rpc--daemon-admin","sections":[],"depth":3},{"title":"Debug logging","local":"debug-logging","sections":[],"depth":3}],"depth":2},{"title":"Daemon-side recorded-move playback","local":"daemon-side-recorded-move-playback","sections":[{"title":"Combined motion + audio","local":"combined-motion--audio","sections":[],"depth":3},{"title":"Record-time audio (sync anchor)","local":"record-time-audio-sync-anchor","sections":[],"depth":3},{"title":"Audio format","local":"audio-format","sections":[],"depth":3},{"title":"Backpressure & cancellation","local":"backpressure--cancellation","sections":[],"depth":3}],"depth":2},{"title":"Live pose stream (30 Hz)","local":"live-pose-stream-30-hz","sections":[],"depth":2},{"title":"Robot onboarding & management","local":"robot-onboarding--management","sections":[],"depth":2},{"title":"Security","local":"security","sections":[],"depth":2},{"title":"Prerequisites","local":"prerequisites","sections":[],"depth":2},{"title":"Working examples","local":"working-examples","sections":[],"depth":2}],"depth":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">"https://pollen-robotics-reachy-mini-central.hf.space"</span>, <span class="hljs-comment">// default</span> | |
| <span class="hljs-attr">clientId</span>: <span class="hljs-string">"my-app"</span>, <span class="hljs-comment">// optional — OAuth client / app id</span> | |
| <span class="hljs-attr">appName</span>: <span class="hljs-string">"My App"</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'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'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">'disconnected'</span> ──<span class="hljs-function"><span class="hljs-title">connect</span>()──▸ <span class="hljs-string">'connected'</span> ──<span class="hljs-title">startSession</span>()──▸ <span class="hljs-string">'streaming'</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<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<{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<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<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<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<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<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<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<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<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<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<{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<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<{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<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<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<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">"@pollen-robotics/reachy-mini-sdk"</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 & 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:<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">"reachy-log"</span>, <span class="hljs-string">"debug"</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">"@pollen-robotics/reachy-mini-sdk"</span>; | |
| <span class="hljs-title function_">setLogLevel</span>(<span class="hljs-string">"debug"</span>); <span class="hljs-comment">// "debug" | "info" | "warn" | "error" | "silent"</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">"happy wave"</span>, | |
| <span class="hljs-attr">onProgress</span>: <span class="hljs-function">(<span class="hljs-params">p</span>) =></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>) =></span> { <span class="hljs-comment">/* sync anchor */</span> }, | |
| }); | |
| <span class="hljs-comment">// result is { finished: true } | { cancelled: true } | { error: "..." }</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">"song"</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 & 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">"state"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =></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 & 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">"Marvin"</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's list until set up again). The session may</span> | |
| <span class="hljs-comment">// tear down right after the ack — that'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.