Data
Tracks
A track is a topic — a single stream of time-stamped data produced by one source. "Left wrist camera", "arm joint positions", "IMU on the base", "operator narration" — each is a track.
A vuer-m3u track is defined by five load-bearing fields:
interface Track {
id: string // stable identifier (usually a UUID or short slug)
name: string // human-readable label
path: string // topic path — "robot/joint_positions"
dtype: DtypeId // data-type identifier — "joint_angles"
src: string // m3u8 URL pointing to the chunks
}The fields, in detail
id
Stable, unique identifier — typically a server-assigned UUID. Used as a React key and for referring back to the track from UI state (hidden flags, selection, etc.). Don't encode semantic information here; use path and dtype for that.
name
Human-readable label for tree / UI display. Often the last segment of path, but can differ (e.g. path: "robot/arm/qpos", name: "Arm joint positions (left)").
path
Topic identity + hierarchy key. /-separated; segments form a tree.
"robot/joint_positions"
"robot/arm/qpos"
"robot/arm/effort"
"cameras/wrist"
"cameras/scene"
"events"The timeline synthesizes groups from path prefixes automatically — no parentId wiring. See Timeline Authoring for how prefix-based tree derivation works.
On the server (dreamlake-py), path is the primary organizing axis:
episode.track("robot/joint_positions", dtype="joint_angles")
episode.track("cameras/wrist", dtype="video")dtype
Data-type identifier — the bridge between "what the data is" and "how it's drawn." Must match a registered DtypeSpec. dtype contracts:
- Chunk format — what file extension the m3u8 references (
jsonl,vtt,ts, …) - JSONL line shape —
{ts, data: number[]},{ts, te, label, ...}, etc. - Unit / range defaults — merged into lane props
The server does NOT enforce the dtype contract. If your chunks don't match the schema, the view breaks — that's on the author.
vuer-m3u ships 13 built-in dtypes. Register custom ones with registerDtype().
src
m3u8 playlist URL. Standard HLS; chunk format inferred from file extension. vuer-m3u parses, fetches, decodes, prefetches, and caches — see M3U8 Transport.
Server Track vs Client TrackRow
Server-side records (see the dreamlake-py SDK) carry the core identity fields. The client extends this into a TrackRow that adds purely presentational overrides — these never travel back to the server:
// Client-side — used in TimelineConfig.tracks
interface TrackRow {
// Identity fields (1:1 with server Track)
id: string
path: string
dtype: DtypeId
src?: string // OR data — mutually exclusive
data?: unknown[]
// Presentation overrides (client-only)
name?: string // tree-label override — default is last segment of path
visible?: boolean
height?: number
color?: string
icon?: string
props?: Record<string, unknown> // merged on top of dtype.defaults
}name, color, icon, height, visible, props are all per-visualization overrides — they don't belong on the server.
Inline data
src points at an m3u8; data supplies inline chunks directly. Exactly one must be provided — runtime validated.
// src-based — fetches + decodes at playhead time
{ id: 'a', path: 'robot/arm', dtype: 'joint_angles', src: '/arm.m3u8' }
// Inline — for small datasets (annotations, markers, test fixtures)
{ id: 'e', path: 'events', dtype: 'marker_event',
data: [
{ ts: 1.0, label: 'start' },
{ ts: 5.0, label: 'grasp', color: 'green' },
{ ts: 9.5, label: 'release' },
] }Inline data renders synchronously at mount; src-backed tracks lazy-load as the playhead reaches each segment.
Path conventions
- Use
/as the separator. Empty segments ("a//b"or leading/trailing/) are rejected at validation. - Prefer lower_snake_case leaves —
"sensors/imu_base"not"sensors/IMU_Base". - Keep paths stable — they're topic identifiers, not display labels. Rename via
nameif the display name needs to change. - Depth is unbounded:
"a/b/c/d/e"works; the tree just shows more levels.
Path-based grouping
TimelineConfig.groups maps path prefixes to per-group presentation overrides:
{
groups: {
'cameras': { name: 'Cameras', color: 'green', icon: 'video' },
'robot': { name: 'Robot state', color: 'blue', icon: 'robot' },
'robot/arm': { name: 'Arm', icon: 'arm' },
},
tracks: [
{ id: 'cam', path: 'cameras/wrist', dtype: 'video', src: '...' },
{ id: 'qpos', path: 'robot/arm/qpos', dtype: 'joint_angles', src: '...' },
],
}Prefixes without an entry still appear in the tree — they just use defaults (path leaf as label, expanded, no icon).
See Timeline Authoring for the full config contract.
Where tracks live in the system
┌── SERVER (dreamlake-py + BSS) ─────────────────────────┐
│ │
│ Track { id, name, path, dtype, src, metadata? } │
│ stored in Postgres + S3 │
│ │
│ path → primary topic identifier │
│ dtype → schema convention (not enforced) │
│ │
└──────────────────────┬─────────────────────────────────┘
│
│ API (GET /episodes/:id/tracks)
▼
┌── CLIENT (vuer-m3u) ───────────────────────────────────┐
│ │
│ TrackRow = Track + { name?, color?, icon?, height?, │
│ visible?, props? } │
│ │
│ TimelineConfig { │
│ container, │
│ tracks: TrackRow[], ← 1:1 with server tracks │
│ groups?: Record<prefix, GroupConfig> │
│ } │
│ │
└────────────────────────────────────────────────────────┘The app composes TimelineConfig from server track records. Most apps just pass the server response through unchanged; some layer app-specific styling (color, icon) on top.
One config, two containers
The same TimelineConfig drives both visualization surfaces:
<TrackerContainer config={config} views={defaultTrackerViews} /> {/* instant state */}
<TimelineContainer config={config} views={defaultTimelineViews} /> {/* trend timeline */}Both containers:
- accept the identical
config: TimelineConfigprop - resolve clock the same way (explicit prop → outer
<ClockProvider>→ new internal one) - dispatch each track through a
viewsmap keyed on itsdtype
Swap containers to switch presentations. Wrap in a shared <ClockProvider> to keep play/seek in sync across both. The track doesn't care which container renders it — that's the point of keeping dtype (data identity) separate from view (presentation).
Next
- Dtypes — the schema registry each track references via its
dtype - M3U8 Transport — what
srcpoints at and how the library fetches chunks - Views — instant-state rendering (
<TrackerContainer>+ standalone views) - Timeline Authoring — trend rendering (
<TimelineContainer>+ lanes)