Microduck Studio docs
A preview shows what you asked a duck to do, and a measurement in physics shows what a simulated duck does with it. These docs take you from your first policy on the phone to a measured result that someone else can reproduce.
Getting started
You do not need a robot, an account or a network connection to start: the policies and recorded motions that come with the app are enough to explore.
- Install the beta. There is no App Store release yet, so get Apple's free TestFlight app and then join the Microduck Studio beta on your iPhone, iPad or Mac.
- Drive it. The app opens on Play, where the left stick walks the duck, the right one turns it, and the buttons between them play moves Pollen trained, like the roulade. It all runs in physics on your phone. The app has five tabs: Play, Learn, Behaviours, Studio and My Microduck.
- Follow Learn. The Learn tab is nine short lessons, from driving to how Microduck is trained to tuning a network. Each one has a button that opens the real screen, and the last one turns on every tool.
- Explore a policy. Open Behaviours and choose a policy from Released by Pollen Robotics. With every tool on, its detail shows the network's structure, and Probe this network shows how its inputs affect its actions.
- Play or make a motion. Open Studio to find motions and scenes, then play a recorded motion or create a draft and edit its keyframes. The preview shows the poses you asked for, not what a robot would do with them.
- Be the judge. In Studio → Compare, watch two networks, motions or sequences side by side and pick the better one, or run a tournament of up to eight and crown a champion. This is the human-feedback half of RLHF: your picks are the labels. Once you have enough of them, Train from your picks learns your taste on the phone and uses it to re-weight Pollen's reward for a training run on Hugging Face, and Settings decides whether picks may be shared.
- Train a duck to shoot. In Studio → Play, build the controller that walks the duck to a ball, lines up, dribbles and switches to Pollen's kick, then train its numbers against goals scored in physics on your phone. It is a search over the controller, not reinforcement learning: Pollen's networks never change.
- Play Duck Soccer. It has a proper pitch, smarter teams that pass, press and make runs, and a camera that keeps your duck and the ball in view. Put your own motions, sequences or trained shooter on Shoot, Pass, Special and Celebrate. Practice mode has five skill challenges with stars, and the water polo finale floods the last minute, so the ducks swim and the beak peck shoots.
- Measure it in physics. Your phone is a bench already. For a faster one, follow the bench setup guide to run it on a computer, then type the address it prints into the app.
Simulation comes first. Nothing here has run on hardware, and the policy inspector, the motion previews and the physics bench each answer a different question. Read what the results mean before you treat a preview or a score as evidence about a real robot.
Connect a real duck
The same app drives a real Microduck through a small bridge on a computer next to the duck, often the duck's own. The bridge comes with OpenCastor, version 3.7 or newer.
- Start the bridge. On that computer:
To have it start on its own,pip install -U "opencastor>=3.7" castor duck # finds the duck, checks it, configures it castor duck --bridge # the bridge the app connects to, port 7788castor up --archetype microduckinstalls it as a service. The bridge prints a token and also saves it in~/.microduck-bridge-token. - Connect. In the app, open My Microduck → Connect to your duck's
computer, type the computer's address (for example
duck.local) and the token, and tap Connect. The sticks on Play now drive the real duck. Switch its Bluetooth pad off first: the robot obeys whichever sends last, and an idle pad keeps sending zeros (Pollen's robotd design). - Show the camera. On the camera card, type the duck's camera
address (for example
duck.local:8080) and tap Show the camera. Turn on Read Duck Cards and the app reads a row of printed cards it can see, ready to open in the plan editor or in Packs. - Measure my duck. If you have a duck, the most useful thing you can send us is a Measure my duck report.
Keep the stop within reach the first time anything moves. If the app goes quiet (the Wi-Fi drops, the phone locks, the app closes), the bridge stops the duck by itself. Connect only on your own network, and never expose the bridge to the internet: its token keeps neighbours out by accident, not attackers out on purpose.
Dream-1, with your own key
Dream Labs' Dream-1 drives a robot from its camera and a task you type, such as "walk to the red mat". It is optional and uses your own Dream Labs account, so Dream Labs bills your key.
- What you need. A Dream Labs key (
dl_live_...) from platform.dreamlabs.ai, a connected duck, and a Dream-1 model fine-tuned on recordings of your duck, because Dream-1's public model drives robot arms, not ducks. A duck model reads the head camera and takes the walk command (vx, vy, vyaw) as both its state and its action, and the app shows only models that fit a duck. - Save the key, once, in one of two places.
- On this device (from build 88): Settings → Dream-1, paste it, tap Check and save the key. The app checks it with Dream Labs, keeps it in the Keychain and sends it only to Dream Labs, so nothing about Dream is needed on the duck's computer.
- On the duck's computer (OpenCastor 3.7 or newer): in the
Dream-1 section of the screen you connected on. The duck's computer
checks the key and keeps it in
~/.dreamlabs/credentials, and the app does not store it. There,castor dream keydoes the same, andcastor dream check --ducklists the models a duck can run.
- Run it. Show the camera, pick your model under Dream-1, type a task, and tap Run Dream-1 on the duck. Every step is held to the sticks' speeds, and the duck stands still while a model starts. A run ends at 60 seconds, when you tap Stop, or on anything unexpected, and it always ends with the duck told to stop.
- No duck model yet? Record one (from build 88). Under Record for
Dream-1, type what you are showing the duck (for example "walk to the red mat"), tap
Start a run, drive it there with the sticks, and tap End the
run. Each run is one example, so record a few dozen and vary them. The app writes
what the camera saw and what you told the duck, ten times a second, and keeps the runs on
this device. Export for Dream Labs makes a .zip, and the rest happens on a
computer: unzip it, open platform.dreamlabs.ai and go to Fine-tuning. Under New
configuration choose the folder, name the state part and the action part
velocity(3 values each) and give the camera the rolehead; then Upload data with the same folder and start a job. Dream Labs charges for the fine-tune, and when it finishes the model appears in Dream-1 here. - No robot? Use the simulated duck (iOS 18 or macOS 15). On Play, with the simulated duck, tap Dream-1: the app draws what the duck's head camera would see on a practice mat with a red mat, a blue mat and a ball, from the real camera's position. Recording and Dream-1 work on it exactly as on a robot, but a model trained only on these pictures learns this mat, not your floor.
- What leaves your home. While Dream-1 runs, whichever of the two holds the key sends Dream Labs the duck's last few camera pictures, its last walk command and your task, about every two seconds, and it sends nothing when Dream-1 is not running. See the privacy policy.
Inspect a policy
Start in Behaviours, which groups policies by where they came from: Released by Pollen Robotics and From elsewhere. The app checks the network's fingerprint, so renaming a file does not make it an official release.
Bring your own network
Open an .onnx file from Files, AirDrop it to your device, or
import a public model from its HTTPS URL. The URL has to be a direct download of
the model file, because a repository's web page is not the model itself.
A file the app refuses stays in your library. Its detail gives the reason beside the operations, layer widths, parameter count and tensor names the app found, which gives you something concrete to check in your export.
Probe what it listens to
Choose Probe this network, start from an observation preset, then change an input and compare the actions, joint targets and travel limits. The input's z-score shows how far it sits from the network's training distribution, and the sensitivity ranking shows which inputs affect its output.
A probe is a forward pass through the network, not a walk. To see a policy walk in a simulated world, run it on the physics bench.
Write a motion
In Studio you can create a motion by posing the robot and editing its keyframes, or open Draft with words, choose Motion and describe what you want. The model's draft goes through the same joint and travel checks as a motion you write yourself, and then it opens for you to edit.
Then choose a scene and preview the result: the preview interpolates your poses and times, but it does not calculate whether the robot could perform the move. The motion's Sim to real row records how far it has got: written, previewed or run on a bench.
Export your authored motion as .duckmove to keep a copy or share it.
A .duckintent is a different thing, a recorded policy run in physics, and
file formats has the distinction. When you are ready to check a
measured result, reproduce a published number.
Evaluations
One good run is an anecdote. An evaluation is a number somebody else can check.
Microduck Studio takes its evaluation vocabulary from inspect-robots, an open source framework for evaluating physical AI, whose one big idea is that a robotics evaluation has two swappable inputs where a language model evaluation has one. A policy turns an observation into actions, and an embodiment is the body and the world that executes them. A task is defined against neither: it is a set of scenes, a set of scorers, one horizon, and a number of epochs with a rule for reducing them.
In the app that becomes a setup screen with four presets, and a preset is a whole plan rather than a starting point. Picking one fills the scenes, what varies between the epochs of a scene, the rule for reducing them, the horizon and the scorers. Two of the four walk a network under a command schedule from drop heights it was never tuned on, and the other two run one episode per published cell of a challenge grid, scored by the challenge harness rather than by the app.
That leaves two things to pick: the thing being evaluated and where it runs. The thing being evaluated is a network from your library, named by digest, or a published challenge entrant. Where it runs is an embodiment, either a bench on your network, named and carrying its world's digest, or this phone's own bench. A real Microduck is on that list and cannot be chosen yet, because an evaluation needs a world it can reset and score, and a real duck on a floor is not that.
So where do you set the number of epochs, the reducer or the seed? You don't. There is no epoch count, no reducer picker and no seed field on that screen, because a control that could disagree with the preset would be a second opinion about what the preset means, and the four presets are the whole argument.
What comes back is an EvalLog: one immutable JSON file that carries the whole specification beside the results, so a number can always be traced back to the run that produced it. It records the task, the policy, the embodiment and its world, the horizon, when it started and finished, the metric for every scorer, and every scene's per epoch scores as well as the reduced one. Trials that errored are recorded and never scored, and a run where every trial errored is a failure rather than a zero.
What it does not record is a seed, and the file says so in its own words:
No seed was recorded, because no route on this bench reads one and a seed here would name a control nothing used. What varies between the epochs of a scene is the height the duck is dropped from, and every height is listed.
One of inspect-robots' reducers is missing here for the same kind of reason, and the app says which and why:
pass at k is not offered. It estimates how often a success would appear in k independent draws, and these epochs are not draws. They are a fixed list of drop heights the bench walks in order, so the estimator's assumption is false here and its number would look like a probability without being one.
- Policy
- Observation in, actions out. Named by digest, not by filename, because
anybody can call a file
alpha_walking.onnx. - Embodiment
- The body and the world. A bench on your network, or this phone's own bench. A real Microduck is named and unavailable: evaluations run in physics only.
- Task
- Scenes, scorers, one horizon, and epochs with a reducer: mean, median, max, min or mode.
- Scorer
- A pure reader of the recorded trajectory. It cannot change what happened, only report on it.
- EvalLog
- Version 1, strict JSON, sorted keys, non finite floats written as null. The filename is the task slug and a short hash.
What the app does not do is run inspect-robots, because an iPhone cannot run Python. The rollouts happen on a bench, the phone's own or one on your network, the scoring reads the trajectory the bench recorded, and the file is written to inspect-robots' schema rather than by inspect-robots' code. That distinction matters enough that it is tested rather than promised: a check in the app's repository writes a log, reads it back with the real library, and fails if their reader refuses it.
The field that would carry inspect-robots' version instead carries a sentence naming Microduck Studio and saying that no inspect-robots ran. Their own viewer prints that field in its header, so a stranger who renders one of these logs is told, in their own report, what wrote it.
You can save an evaluation you have run, export it as its JSON, render it as a
self contained report the app draws itself, send it through the share sheet, or
publish it to a Hugging Face dataset under your own account, and a log written
somewhere else opens on the phone too. On a computer,
inspect-robots view LOG.json renders the same file as a standalone
HTML report, and read_eval_log will refuse it outright if the schema
is wrong, which is the point of writing the real one.
An EvalLog is a record, not a submission. The two challenge screens are the only path onto either leaderboard, and a log of a challenge run puts nothing on one.
The bench
The bench is the piece that turns a preview into a measurement: a small Node program that runs MuJoCo on a computer you already own, a Mac, a PC or a Raspberry Pi, and that the app talks to over your own network.
It is a desktop download for a Mac, a PC or a Raspberry Pi, and it does not run on the phone.
Starting it
# Windows: right-click start.ps1, then Run with PowerShell
# macOS and Linux:
./start.sh
The first start installs MuJoCo and onnxruntime and takes a couple of minutes, and every start after that takes seconds. It prints the address to type into the app, or to curl.
Use the Tailscale address it prints, not a Wi-Fi one. A 192.168 address works right up until your phone prefers cellular or you leave the room, and then it fails looking exactly like a bench that has gone down.
What travels with it
The bundle carries scene.mjb, the physics world every
measurement is made against, and its digest. Why ship the world and not just its
filename? Because a number measured against a different world is not comparable to
one measured here.
3f8c9ab9b409ba74c73c30179d5f7c12b025f631693f9eec78d80dca242547be
That is the plant every number on this page was produced against. It is printed in full rather than shortened, because a shortened digest is a digest nobody can compare.
The bench inside the phone
The app also carries the bench and serves it to itself over HTTP on 127.0.0.1, so that it has one client for one protocol rather than two. Its speed on a phone has never been measured. The figure that gets quoted for the core came off a desktop browser on a Raspberry Pi 5, which is not an iPhone, so the app reports the tick its own bench measures wherever it actually runs rather than quoting somebody else's machine.
You can drive the same core in Safari without the app, and the phone bench probe tells you what your own phone can and cannot do with it. It is a probe and not a bench, there so you find out before the app asks you to trust a number.
Challenges
There are two published challenges, both still open and unmet, and both scored against the same compiled world by the harness in duckbench. Pollen's own Arena runs races of its own on Hugging Face, and Race in Pollen's Arena is our companion to it.
Stairs
Get the duck from the floor onto a step in simulation and leave it standing there, upright on the tread with both feet resting on it, fifty ticks after the move ends.
The criterion, as sim/climb_score.mjs exports it:
honest: at the scored instant the trunk is upright, past the riser at
x > 120 mm, more than 95 mm above the tread, with at least two feet resting
on a tread (past the riser, within 5 mm below to 45 mm above it, and within
3 mm of a step), and the duck never left the 340 mm-wide flight at any tick
of the episode. stable: honest, and upright for at least 45 of the 50 tail
ticks.
The bar is 7 of the 9 core cells, cleared and still standing. Nothing has met it: the record is 5 of 9 at a 60 mm rise, and round six measured the trunk's peak height as the reason 7 was never reachable at this scale. A robot with a 10 mm shorter neck link reached a 6 of 9 ceiling in simulation on 2026-09-07, still short of 7.
A submission is scored over fourteen cells of which nine are the core every published number is quoted against. Nineteen rows are published: sixteen entries, two of them reference oracles, and three reference controls, with the intent files they name, byte for byte as the audit ran them. The negative result is published rather than hidden.
Ball
A ball is put down in front of the duck, between 450 mm and 1.2 m away and up to 40 degrees off its heading, and the duck has to reach it and move it in simulation without falling over.
The criterion, restated from sim/chase_score.mjs: chased means
the duck touched the ball, meaning any duck geometry came within 3 mm of it at
any tick, and the ball finished at least 100 mm further along the duck's initial
heading than it started, and the duck was still upright at the end of the
episode. Stable means chased, and upright for at least 45 of the 50 tail
ticks.
Four control rows and no entries. Standing still and both of Pollen's kick policies chase nothing at all; walking straight ahead takes 4 of the 9 core cells and 1 of the 5 extended, and every cell the controls miss needs steering, which nothing bundled does.
The nine reward terms are the reward these policies were trained on, and they are reported so an edit can be watched moving them. They are not the verdict: a shaped sum of nine weighted terms is not a thing a person can hold in their head, and a leaderboard sorted on it would reward a duck that stands beautifully still.
This challenge carries one caveat: the kick config was trained against a 70 mm, 15 g ball and this plant's ball is 100 mm and 30 g, so a speed in metres per second measured here is not comparable with anything Pollen published.
Both grids, both entrant corpora and every published score live in the datasets below. The app scores an entrant on the published grid, through the harness's own episode function, against the same compiled world, and it says so beside every number, because a score measured somewhere else is not a score on this leaderboard.
Stairs challenge dataset
Ball challenge dataset
Submit a result
A submission is not a number. The file holds the move, all fourteen per-cell answers unrounded, the bench's plant digest and the date. It is written to this device and nothing is sent until you pick where it goes.
Where it goes is a GitHub issue on duckbench with the file attached, which is the submission a maintainer sees. Publishing the same file to a Hugging Face dataset under your own account, under CC BY 4.0, is your archive, and it submits nothing to anyone.
Packs and Measure my duck
A pack is a game for Duck Cards, set for a grade band: a board, the cards in play, challenges that ramp from easy to hard, and lessons. Four ship with the app, from PreK to grade 8, and the pack pages print them.
- Play a challenge. Open Packs, choose a pack and a challenge, and lay a row from the pack's cards, or take the row the camera read. The board plays it step by step and says solved, the score and par.
- Run it on the duck. Run on the duck sends the same row as a plan. On a real duck its Walk and Turn cards are timed, and the screen says so: a timed card does not land on a square.
- Long rows play in chapters. A row of more than 7 Walk and Turn cards is split into chapters, and the board rings each nest, the square the duck goes back on, and which way it faces. Run on the duck stops at each nest until you put the duck there and press The duck is on the nest.
- Design a level. Tap squares for the start, goal, walls and gates, and par
is worked out for you. Challenges save into your class pack, which shares as a
.duckpack. - Measure my duck. Packs, then Measure my duck, is the
one screen that runs closed-loop cards on a robot, and it opens by saying it is a test. Tape a
grid of 30 cm squares, five long and three wide. The duck walks one to four squares and turns
four quarter turns, and you type where it ended, read off the tape. It judges your marks
against the same criteria as the simulator and shares a
duck-measure/0report.
For why closed-loop cards are off on real ducks, with every number and every row, read the evidence.
Reproduce a published number
Five steps, and none of them needs the app.
- Get the bench and check it. Download the bundle above, run
shasum -a 256 duckbench-bundle.zip, and compare the answer with the digest linked beside the download. - Start it. Run
./start.shon macOS or Linux; the first start installs MuJoCo and onnxruntime. - Check the plant. Run
curl -s http://ADDRESS/healthand read the plant name and digest out of the answer. If the digest is not the one printed above, stop, because whatever you measure will not be comparable with anything on this page, and the honest thing to do with an incomparable number is not to publish it. - Get an entrant. The published intents live in the two
datasets, and the same files with the same digests are in
duckbench/challenge/intents. - Score it, and compare the right column. Post the entrant
to
/climbor/chaseone cell at a time, taking the cell list from/climb/gridor/chase/grid, or run the harness directly. The stairs table ranks on cleared core cells and prints the stable count beside it, and the ball table ranks on chased and prints stable beside it. Quoting one without the other is quoting a number the table does not rank by.
The app calls eighteen bench routes and no others, and the bench inside the phone answers exactly that set and returns 404 for anything else, so a route that exists on one bench and not the other cannot hide.
File formats
The app reads back every format it writes. That was not always true, and fixing it is why we document them here.
| Extension | What it holds |
|---|---|
.duckmove |
A motion: keyframe times and one angle per joint. Poses and times interpolated, with no physics run. |
.duckplan |
A fetch plan: the measured object and what was asked for. The steps are derived when it is read, so a plan cannot go stale and disagree with the app that opens it. |
.duckintent |
A recording of a policy running in physics, with the world it was recorded in named inside it. |
.onnx |
A trained policy. The app checks its input and output widths against the robot the app knows about, and tells you exactly why it refuses one that does not match. Recurrent policies, which carry their memory in extra inputs and outputs (Pollen's page on them), are not supported yet. |
.json |
A formal evaluation: the task, the policy, the embodiment and its world
digest, the horizon, every scorer's metric, and every scene's per epoch
scores. inspect-robots' own schema, so read_eval_log and
inspect-robots view open it unchanged. |
What is honest about this
Two claims are easy to make about a robot app, and neither one is made here. There are four more things worth saying out loud.
A preview is not a result. A preview on a phone is what you asked for, not what the robot would do. We asked the bench, and the four authored stair motions in this app get up their flight 0 times in 16. Nothing on a phone screen would ever have told you that.
A success rate is not an answer. The bench's criterion is "ends standing", which a duck standing perfectly still passes every time, so the distance travelled is always shown beside the rate, and a leaderboard is never sorted on a shaped sum of reward terms.
Nothing here has run on hardware. Every number on this page and in the app came out of simulation, in a named world, with that world's digest attached to it. When a real Microduck arrives, none of these numbers transfers for free.
The bench inside the phone has never been speed measured on a phone. The figure that gets quoted for the core was measured in a desktop browser on a Raspberry Pi 5, which is not an iPhone, and the app does not pretend otherwise: it reports the tick its own bench measures where it actually runs.
The ball challenge's numbers are about this ball. The kick config was trained against a 70 mm, 15 g ball and this plant's ball is 100 mm and 30 g, so a speed measured here is not comparable with anything Pollen published.
This site borrows a framework's vocabulary, one way. Microduck Studio adopts inspect-robots' evaluation vocabulary and log format, but it is not made by, endorsed by, or affiliated with inspect-robots, and no inspect-robots code runs in the app.
Downloads and links
Everything is public: our four repositories, the two challenge datasets, the bench bundle, the phone bench probe, and the two projects this one stands on.
Get the app
- TestFlight beta Free, for iPhone and iPad (iOS 17 or later) and Mac (macOS 14 or later), through Apple's TestFlight app.
- App Store: not yet, and there is no date.
The code
- duck-studio The app itself, and StudioKit, the package that holds everything it computes. Apache-2.0.
- duckkit The Microduck as pure Swift: its joints, their travel, its kinematics. The app pins a released tag of it.
- duckbench The bench and the challenge harness: the physics, and the scoring functions the challenge numbers came out of.
- microduck-com This site: static HTML and CSS, plus the books' own scripts, with no build step, so you can read the whole thing.
The data
- Stairs challenge The grid, the entrants, the published scores, and the round that measured why the bar is out of reach.
- Ball challenge The grid, the entrants and the published scores.
- Submit a result An issue on duckbench with the submission file attached, which is the path a maintainer sees.
The tools
- The bench bundle 11.7 MB, with its sha256. MuJoCo, onnxruntime and the compiled world every number is measured against, as a desktop download that does not run on the phone.
- Phone bench probe The same core in Safari, so you can see what your own phone manages before you trust a number from it.
- inspect-robots and its documentation The evaluation framework whose vocabulary and log format the app adopts. MIT licensed, and not ours.
Elsewhere
- Pollen Robotics' Microduck The robot itself, from the people who make it. This app is not theirs.
- Privacy No accounts, no analytics, no tracking, and the reason this page loads nothing from anywhere else.