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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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.
  9. 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.

  1. Start the bridge. On that computer:
    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 7788
    To have it start on its own, castor up --archetype microduck installs it as a service. The bridge prints a token and also saves it in ~/.microduck-bridge-token.
  2. 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).
  3. 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.
  4. 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.

The app collects no data. It makes network requests when you choose to fetch, publish, draft with a service or use a bench, and the privacy policy says where each of them goes.

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.

inspect-robots: the repository · the documentation. MIT licensed. This project adopts its log format and vocabulary and is not made by, endorsed by, or affiliated with it.

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.

Download the bench, 11.7 MB sha256

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.

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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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/0 report.

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.

  1. 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.
  2. Start it. Run ./start.sh on macOS or Linux; the first start installs MuJoCo and onnxruntime.
  3. Check the plant. Run curl -s http://ADDRESS/health and 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.
  4. Get an entrant. The published intents live in the two datasets, and the same files with the same digests are in duckbench/challenge/intents.
  5. Score it, and compare the right column. Post the entrant to /climb or /chase one cell at a time, taking the cell list from /climb/grid or /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.

Every format the app writes and reads back
ExtensionWhat it holds
.duckmove
duck-move/2
A motion: keyframe times and one angle per joint. Poses and times interpolated, with no physics run.
.duckplan
duck-plan/1
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
duck-intent/2
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
EvalLog version 1
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.

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

The code

The data

The tools

Elsewhere