This document explains how the TimeSeries component in Label Studio is built, how its synchronisation with other media works (video / audio), and how the playback cursor logic is implemented.
The goal is to give both humans and LLMs enough context to confidently extend or debug the code.
TimeSeries (MST model + React container)
├── Channels (one per data column) – individual SVG plots
│ ├── Hover tracker (grey) – shows XY under mouse
│ └── Playhead line (customizable) – current playback position
├── MultiChannel (grouped visualization) – multiple channels in single plot
│ ├── Channel Legend – interactive channel visibility controls
│ ├── TimeSeriesVisualizer – unified D3 rendering component
│ └── Automatic color palette – consistent channel colors
├── Overview (small plot at bottom) – brush for zoom / pan
│ └── Playhead line (customizable)
└── Region brushes – user labelled time ranges
TimeSeriesModel (MobX-state-tree). Holds data, view state and actions. Mixin order:SyncableMixin → ObjectBase → PersistentStateMixin → AnnotationMixin → Model.HtxTimeSeriesViewRTS (React) renders overview + channel children.ChannelD3 (being replaced by TimeSeriesVisualizer).MultiChannelModel + HtxMultiChannel component that groups channels together.ChannelD3 for both single and multi-channel views.brushRange.| Field | Type | Meaning |
|---|---|---|
brushRange |
[number, number] |
Visible time window (native units) |
cursorTime |
number \| null |
Current playhead position (native) |
seekTo |
number \| null |
One–shot instruction for overview to centre view |
scale |
number |
Cached zoom factor (forces rerender) |
canvasWidth |
number |
Cached width in px for correct math |
isPlaying & co. |
… | Playback loop state |
cursorcolor |
string |
Hex/colour string for playhead (default: --color-neutral-inverted-surface) |
suppressSync |
boolean |
Temporarily disable sync events (during overview drag) |
Native units = ms when timeformat is a date, otherwise raw numeric indices/seconds.
The MultiChannel tag enables grouping multiple data channels in a single visualization with the following features:
Interactive legend component that allows users to:
* Toggle channel visibility by clicking on legend items
* Highlight channels on hover for better visual focus
* Automatically assigns colors from a predefined palette
palette.js with getChannelColor(index) functionUnified D3-based rendering component that replaces the legacy ChannelD3 approach:
* Supports both single channel and multichannel visualizations
* Handles brush interactions for region creation
* Manages playhead cursor positioning
* Provides consistent rendering logic across MultiChannel and Channel
* Eliminates code duplication between single and multi-channel rendering
<TimeSeries name="ts" value="$timeseries" cursorColor="#ff0000">
<MultiChannel height="300" showAxis={true}>
<Channel column="velocity" legend="Velocity" units="m/s"/>
<Channel column="acceleration" legend="Acceleration" units="m/s²"/>
</MultiChannel>
</TimeSeries>
The SyncableMixin provides a small intra-tab message bus.
sync="groupA". All tags with the same name join one SyncManager.play, pause, seek, speed.seek / play / pause when FF FF_TIMESERIES_SYNC is on.syncSend:emitSeekSync() (fires on updateTR).handleMainAreaClick.suppressSync = true to prevent cursor jumps._handleSeek, _handlePlay, _handlePause which in turncursorTime),_updateViewForTime only if needed.Audio tags honour mute logic so that only one sound is audible.
cursorTime in the model (native units).cursorTime is written by:playbackLoop),_handleSeek (remote seek),_updateViewForTime (local scroll),setCursor) when inside current view.useEffects and D3 – whenever item.cursorTime changes they reposition their SVG playhead line.TimeSeriesVisualizer / legacy ChannelD3)this.playhead = this.main.append('line')
.attr('stroke', parent.cursorcolor)
.attr('x1/x2', this.x(cursorTime))
updatePlayhead(time) hides the line if the time is outside current x.domain() or null.
Note: In modern MultiChannel components, this logic is handled by TimeSeriesVisualizer which provides unified cursor management across all channels.
Identical logic but uses scaled brush coordinate.
brushRange we call setCursor(time) – only cursor moves._updateViewForTime recentres view and may emit sync.isPlaying is true and user clicks, the restartPlaybackFromTime(time) action:playStartPosition and playStartTime to the clicked positionhandleTimeSeriesMainAreaClick() in helpers.js provides unified click handling for both:TimeSeries.jsx)TimeSeriesVisualizer.jsx for MultiChannel)brushstarted), suppressSync is set to true.emitSeekSync() from firing during the drag, keeping cursor fixed.brushended, suppressSync is reset to false (with 0ms delay to let range settle).| Action | Purpose |
|---|---|
updateTR(range) |
Central method to change visible window; triggers rerender + optional sync |
scrollToRegion(r) |
Ensure a labelled region is visible (may expand brush) |
setCursorAndSeek(t) |
Update both cursorTime & seekTo (internal only) |
setCursor(t) |
Update only cursorTime (no brush movement) |
restartPlaybackFromTime(t) |
Restart playback loop from specific time (handles click during playback) |
_updateViewForTime(t) |
Convert time → pixels & adjust brush + cursor |
setSuppressSync(flag) |
Temporarily disable sync emissions |
The helpers.js file contains shared utility functions used across TimeSeries components:
handleTimeSeriesMainAreaClick(event, timeSeriesItem, mainDisplayElement) – Unified click handling logic used by both:setCursor, _updateViewForTime, restartPlaybackFromTime)sparseValues() – Data thinning for performance with large datasetsgetRegionColor() – Color calculation for labeled regionsformatTrackerTime() – Time formatting for tracker displaycheckD3EventLoop() – D3 event loop preventionTagAttrs with MST types.optional, then read item.<attr> in views.MultiChannelModel actions or extend ChannelLegend component.palette.js or add channel-specific color attributes.sparseValues(); thresholds controlled by zoomStep.TimeSeriesVisualizer for custom D3 rendering behaviors.| Term | Meaning |
|---|---|
| Native units | Raw numeric time values used in dataset (ms for dates, seconds/indices otherwise) |
| Relative seconds | Seconds offset from dataset start – format used in sync messages |
| Brush | D3 brush in Overview controlling visible window (brushRange) |
| MultiChannel | Component that groups multiple data channels in a single visualization |
| Channel Legend | Interactive legend component for controlling channel visibility and highlighting |
| TimeSeriesVisualizer | Unified D3-based rendering component that replaces legacy ChannelD3 |
| ChannelD3 | Legacy D3 rendering component (being replaced by TimeSeriesVisualizer) |
| Color Palette | Predefined set of colors automatically assigned to channels |
| Playback Sync | Real-time synchronization between video/audio playback and TimeSeries cursor position |
| Click Handling | Unified logic for processing user clicks on TimeSeries visualizations |