pollen-robotics/microduck-emotions
Microduck Emotions A collection of emotions for the Microduck robot. Each one is a motion and a sound designed together, beat by beat, with the beak opening on the sound, rendered in the physics simulation and validated on the real robot. Every emotion is three files: the motion (emotions/<name>.json, keyframes at 30 fps: head and body offsets played on top of whichever trained policy is active, plus the policy hand-overs, such as the sit that devastated and play dead start)… See the full description on the dataset page: https://huggingface.co/datasets/pollen-robotics/microduck-emotions.
Microduck Emotions
A collection of emotions for the Microduck robot. Each one is a motion and a sound designed together, beat by beat, with the beak opening on the sound, rendered in the physics simulation and validated on the real robot. Every emotion is three files: the motion (emotions/<name>.json, keyframes at 30 fps: head and body offsets played on top of whichever trained policy is active, plus the policy hand-overs, such as the sit that devastated and play dead start), the sound the robot plays (emotions/<name>.wav, 48 kHz mono), and the render with its sound (videos/<name>.mp4). metadata.jsonl indexes them all.
See and hear them
<table> <tr><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/sad.mp4" controls preload="metadata" width="100%"></video></td><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/devastated.mp4" controls preload="metadata" width="100%"></video></td><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/curious.mp4" controls preload="metadata" width="100%"></video></td></tr> <tr><td align="center"><b>sad</b> · 7.5 s</td><td align="center"><b>devastated</b> · 8.0 s</td><td align="center"><b>curious</b> · 3.0 s</td></tr> <tr><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/mmh.mp4" controls preload="metadata" width="100%"></video></td><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/yes.mp4" controls preload="metadata" width="100%"></video></td><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/yesfast.mp4" controls preload="metadata" width="100%"></video></td></tr> <tr><td align="center"><b>mmh</b> · 2.0 s</td><td align="center"><b>yes</b> · 1.5 s</td><td align="center"><b>yes fast</b> · 1.5 s</td></tr> <tr><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/no.mp4" controls preload="metadata" width="100%"></video></td><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/angry.mp4" controls preload="metadata" width="100%"></video></td><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/excited.mp4" controls preload="metadata" width="100%"></video></td></tr> <tr><td align="center"><b>no</b> · 1.8 s</td><td align="center"><b>angry</b> · 2.6 s</td><td align="center"><b>excited</b> · 3.4 s</td></tr> <tr><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/laugh.mp4" controls preload="metadata" width="100%"></video></td><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/mock.mp4" controls preload="metadata" width="100%"></video></td><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/defiant.mp4" controls preload="metadata" width="100%"></video></td></tr> <tr><td align="center"><b>laugh</b> · 3.0 s</td><td align="center"><b>mock</b> · 2.6 s</td><td align="center"><b>defiant</b> · 2.2 s</td></tr> <tr><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/impatient.mp4" controls preload="metadata" width="100%"></video></td><td align="center"><video src="https://huggingface.co/datasets/pollen-robotics/microduck-emotions/resolve/main/videos/playdead.mp4" controls preload="metadata" width="100%"></video></td></tr> <tr><td align="center"><b>impatient</b> · 2.6 s</td><td align="center"><b>play dead</b> · 7.5 s</td></tr> </table>
Bindings and what each one does
Emotion mode = a tap on D-pad Up; short press under 0.6 s, long press held.
Download and use
from huggingface_hub import snapshot_download
root = snapshot_download("pollen-robotics/microduck-emotions", repo_type="dataset")
# root/emotions/yes.json, root/emotions/yes.wav, root/videos/yes.mp4, root/metadata.jsonlimport json
rows = [json.loads(l) for l in open(f"{root}/metadata.jsonl")]
for r in rows: print(r["name"], r["pad"], r["duration_s"], "s:", r["description"])On the robot. The emotions are compiled into the pad daemon of the runtime branch pad-expressions (padd/src/expressions.rs: each emotion has a unit test against its keyframe file here). With that build installed: a tap on D-pad Up switches the pad to emotion mode, where each button plays one emotion (short press / long press, see the pad column); and the daemon's cue port (TCP 7777) plays any of them from a script:
import json, socket
s = socket.create_connection(("192.168.1.29", 7777)); s.sendall(json.dumps({"express": "yes"}).encode() + b"\n")
print(s.recv(4096)) # {"ok": true, "duration": 1.5}The theater player that drives Reachy Mini and Microduck together from one scene file lives in agentic_robot_theater (robot/skit.py, robot/duck_cue.py).
In simulation. tools/lib.py renders an emotion from its keyframes in MuJoCo with the real policies (it imports the microduck simulation stack, duckfilm.py), tools/quack.py is a Python port of the robot's quack synthesizer, tools/combine.py muxes a wav onto a render at a given time. They are here as a reference for making new emotions; see METHOD.md.
The motion format
emotions/<name>.json = {"fps": 30, "keyframes": [...], "measured": {...}}, plus joints_order and pose_joints for play dead. One keyframe per frame:
measured records what the simulation saw (drift, trunk pitch, falls) for the render in videos/.
Real-robot lessons baked in
- Anything that moves the head's mass forward makes the walking policy step forward; the simulation does not show it. Sad was halved in depth and curious lost its head-forward for that reason.
- The beak is a servo: it is gated on "driving", so a sound after the torque goes off (play dead's death quack) plays with the beak shut.
- Sounds were made to be about 20 % quieter than the first drafts; the robot's speaker is loud.
Credits
Designed by Rémi Fabre with Claude Code agents, September 2026, in the microduck_emotions workshop repo (the full history of candidates, rejected takes and reasons). Reachy Mini's side of the films: agentic_robot_theater.
