Templates

Two datasets on the Hub, both real recordings, both public. Open them, look at the bytes, and make yours look the same. Each carries its own .dreamrc at its root — the file you see below is fetched from the dataset, not copied into this page.

templatewhen it is yoursHub
LeRobot with annotation featuresyou are writing a LeRobot dataset and want annotations INSIDE itlive9080/dreamlake-lerobot-annotated
Folder of video + sidecarsyou have recordings and files, and no container at alllive9080/dreamlake-ceramics

Neither template asks you to convert anything into a DreamLake format — the contract is entirely container idioms and existing standards. Copy the shapes; the numbers are ours.

1. LeRobot with annotation features

Thirty seconds of a real egocentric recording, repacked as LeRobot v2.1, carrying every annotation as an ordinary feature of the container. Every number in it was measured on this one recording.

dreamlake-lerobot-annotated/
├── .dreamrc                        ← the visualization, no storage: block
├── meta/
│   ├── info.json                   ← features + shapes: the whole contract
│   ├── episodes.jsonl
│   ├── tasks.jsonl
│   └── subtasks.jsonl              ← the label table for subtask_index
├── data/chunk-000/episode_000000.parquet
└── videos/chunk-000/observation.images.ego/episode_000000.mp4

What each feature declares, and what the .dreamrc binds it to:

feature in meta/info.jsondtype / shapebound as
observation.images.egovideo [1080,1920,3]videoStack.cameras → camera tile
observation.keypoints_2d.left.egofloat32 [21,3]videoStack.overlays with as: keypoints → hand skeleton
observation.keypoints_2d.right.egofloat32 [21,3]the same, second hand
subtask_index + meta/subtasks.jsonlint64 [1] + label tabletracks: [{ field: subtask_index, as: segments }] → labelled blocks

Two details are load-bearing, and both are easy to get wrong:

  • Which camera a hand belongs to is stated in the config, not in the name. on: observation.images.ego pins the overlay to that camera, and that is the only thing that pairs them — the feature could be called anything. Pixel space comes from the camera it is pinned to. (With one camera in the episode there is nothing to disambiguate, so on is optional here.)
  • A labelled span is an index column plus a label table. That is LeRobot's own task_index + meta/tasks.jsonl pattern, one level down. The join happens when a binding asks for segments; without the table there is nothing to label the spans with, and the read says so.

A third detail is about honesty rather than mechanics: a frame where the tracker found nothing is NaN, not zeros. Zero is a real pixel — the top-left corner — and a viewer cannot tell a fabricated origin from a measured one, so zeros draw a collapsed skeleton in the corner on every undetected frame. 279 of this episode's 900 left-hand frames are NaN, and they render as nothing at all.

Resolving dataset…

2. A folder of video and sidecar files

The zero-conversion route: no container, no manifest, no metadata file. One directory per episode, files named after the tracks they carry.

dreamlake-ceramics/
├── .dreamrc
└── episodes/
    ├── episode_a/
    │   ├── cam_ego.mp4             ← track name = basename
    │   └── annotations/
    │       ├── hand_keypoints.json ← COCO keypoints
    │       └── subtasks.vtt        ← WebVTT
    └── episode_b/                  ← the same recording, second half
        ├── cam_ego.mp4
        └── annotations/…

Every file is in a format that already existed before we did. Three conventions PLACE them — one episode per folder, basename is the track name, annotations/ is searched (how) — and not one of them says what a file contains. hand_keypoints.json is catalogued as a file with a .json extension; it becomes a skeleton because the .dreamrc binds it as: keypoints. Rename every file and the parse result is identical; that is the line between a convention and a format.

Both episodes carry the same track set here, but they need not: a dataset where some episodes are annotated and others are not is the normal case, and it renders as one.

Resolving dataset…

Upstream datasets, untouched

The two templates above are datasets we built. The more interesting claim is the other one: a dataset published by somebody else needs no modification at all. Two of the most-downloaded LeRobot datasets, forked into our namespace with exactly one file added:

forkupstreamfiles changed
live9080/pushtlerobot/pusht — 206 episodes+1 (.dreamrc)
live9080/svla_so101_pickplacelerobot/svla_so101_pickplace — SO-101, two cameras+1 (.dreamrc)

Compare the file trees on the Hub: every parquet, every mp4 and every metadata file is byte-identical to upstream. The .dreamrc describes how to look at the data; it never asks the data to change. Both render in the gallery.

Not ours, and just as valid

Two categories that need no template of ours, because the public dataset already has the shape — point a .dreamrc at them and read the contract for the rule each one satisfies:

you wanta real dataset that does it
depth mapsjuyil/libero_spatial_lerobot_depthfloat32 [256,256,1] named depth_primary
point cloudsrishabhrj11/gym-xarm-pointcloudfloat32 [512,6], xyz + rgb
many cameras + bimanual statelerobot/aloha_static_coffee — four cameras, 14-DoF

The gallery renders all of them, plus zarr and MCAP.

Where the .dreamrc goes

Both templates above own their data, so the file sits at the dataset root with no storage: block — the app injects the root it found the file in. The gallery's third-party entries are the other case: read-only data, so each config lives in live9080/dreamlake-configs and names its dataset in its own storage: block, which always wins over an injected root. The full rule: spec.

Check before you ship

bash
npx tsx scripts/check-dreamrc.mts hf:your-name/your-dataset

It resolves the dataset the way the app does: with no config it prints the inventory — every field with the dtype and shape its container reported — and with one it decodes every binding and prints what came back. That is how you find out whether the [21,3] column you called a skeleton really is one, before anybody looks at a panel. It also takes an https:// root, or a local .dreamrc file before you upload anything.