OPERATOR

PlaybackControls

Review history

Play, pause, scrub, step between event times, select a playback speed, or return to the supplied live reference. The example owns a simulated clock and stops at the end of its fixed training window.

Playback position2026-10-01 12:00:25.000 UTC
Start: 2026-10-01 12:00:00.000 UTCEnd: 2026-10-01 12:01:00.000 UTC

Training clock · Fixed live reference: 2026-10-01 12:01:00 UTC.

Controlled usage

All timestamps use Unix epoch milliseconds. The component displays the date, time, milliseconds and UTC zone. It requests changes; your application supplies the accepted cursor and playback state.

<PlaybackControls
  start={windowStart}
  end={windowEnd}
  value={cursor}
  playing={playing}
  onValueChange={setCursor}
  onPlayingChange={setPlaying}
  eventTimes={events.map((event) => event.time)}
  playbackRate={rate}
  onPlaybackRateChange={setRate}
  liveTime={liveTime}
  followingLive={followingLive}
  onReturnToLive={returnToLive}
/>

Scrubbing and event stepping call onPlayingChange(false) before onValueChange(nextTime). Event times are sorted and deduplicated; invalid dates, times outside the range and the current event time are skipped. Buttons disable when there is no earlier or later event.

The application owns its clock, media seeks, playback completion, live reference, range updates and synchronization. onReturnToLive can replace the review window when the supplied live reference lies outside it. Changes are never accepted optimistically by the component.

Optional controls

  • Supply eventTimes to show event stepping, including disabled steps for an empty list.
  • Supply onPlaybackRateChange to show speed selection. Defaults are 0.5×, 1×, 2× and 4×. Speeds are listed in ascending order, and a supplied playbackRate that is not offered is shown in order among them.
  • Supply onReturnToLive and a valid liveTime to show Return to live.
  • Set followingLive to show the supplied follow state and disable Return to live.

Unavailable range

Invalid or equal bounds disable the transport and explain the problem. An invalid cursor displays “Unknown playback position.” A cursor outside the range clamps for display and disables transport until the caller supplies an in-range value. Epoch zero is valid.

Single observation position2026-10-01 12:00:00.000 UTC
Start: 2026-10-01 12:00:00.000 UTCEnd: 2026-10-01 12:00:00.000 UTC

A single timestamp is available. Playback requires a time range.

Disabled

Playback position2026-10-01 12:00:25.000 UTC
Start: 2026-10-01 12:00:00.000 UTCEnd: 2026-10-01 12:01:00.000 UTC

Training clock · Fixed live reference: 2026-10-01 12:01:00 UTC.

Props

PROPTYPEDEFAULTREQUIRED
start

Inclusive range start in Unix epoch milliseconds. Must be a valid date before end; zero is valid.

number—yes
end

Inclusive range end in Unix epoch milliseconds. Invalid or single-point ranges disable transport with explanatory text.

number—yes
value

Controlled cursor in Unix epoch milliseconds. Out-of-range values clamp for display and disable transport; invalid dates display an unknown position.

number—yes
playing

Caller-owned playback state. The component owns no clock and makes no optimistic state changes.

boolean—yes
onValueChange

(value: number) => void. Requests a valid in-range timestamp after requesting pause.

function—yes
onPlayingChange

(playing: boolean) => void. Requests play/pause. Scrubbing and event stepping call this with false before onValueChange.

function—yes
playbackRate

Controlled positive finite speed multiplier. A valid supplied rate remains visible, in order, even when absent from playbackRates; invalid rates display Unknown rate.

number1no
playbackRates

Readonly number array of offered speed multipliers. Defaults to [0.5, 1, 2, 4]. Non-positive/non-finite rates and duplicates are omitted. Listed in ascending order.

array—no
onPlaybackRateChange

(rate: number) => void. Supplying this callback shows the controlled speed selector.

function—no
eventTimes

Readonly epoch-millisecond array. Supplying it shows previous/next event buttons. Times are sorted, deduplicated and filtered to valid dates within the range; stepping skips the current time.

array—no
liveTime

Application-supplied live reference in epoch milliseconds. May lie outside the review range; the application owns any range change.

number—no
followingLive

Shows Following live and disables Return to live while the caller follows its reference.

booleanfalseno
onReturnToLive

() => void. Shows Return to live only when liveTime is a valid date. Requests no cursor, range or playing change itself.

function—no
disabled

Disables all transport actions and removes them from sequential keyboard navigation.

booleanfalseno
label

Accessible root-group name. Also names the position slider and playback-speed selector.

string"Playback"no
classNamestring—no

Accessibility

  • The forwarded ref points to the named root group. Native div attributes (id, data-*, style) reach it, and a supplied aria-describedby is kept alongside the unavailable message.
  • Buttons use native keyboard activation; the speed selector uses the shared Select primitive.
  • The Slider thumb announces a formatted UTC timestamp. Each arrow key moves it by a readable step of about a two-hundredth of the window (10 seconds in a 30-minute window, 50 ms in a 10-second clip), stopping exactly at either end. Page Up and Page Down move a tenth of the window. Home and End request the exact range endpoints, and event stepping keeps exact milliseconds.
  • Scrubbing preserves keyboard focus as accepted timestamps change. When an accepted request makes the focused button unavailable (stepping onto the first or last event, Return to live once following), focus moves to the other event step, else Play or Pause, else the group.
  • Invalid inputs are described on the group and slider; disabled actions leave the tab order.
  • Shared controls provide minimum 24px targets and the OPERATOR focus indicator.

Manifest entry

<PlaybackControls start={start} end={end} value={cursor} playing={playing} onValueChange={setCursor} onPlayingChange={setPlaying} eventTimes={eventTimes} playbackRate={rate} onPlaybackRateChange={setRate} liveTime={liveTime} followingLive={followingLive} onReturnToLive={returnToLive} />
View agent manifest
{
  "description": "Controlled UTC timestamp transport with a bounded scrubber, play/pause, optional event stepping, playback speed and return to a supplied live reference.",
  "props": {
    "start": {
      "type": "number",
      "required": true,
      "description": "Inclusive range start in Unix epoch milliseconds. Must be a valid date before end; zero is valid."
    },
    "end": {
      "type": "number",
      "required": true,
      "description": "Inclusive range end in Unix epoch milliseconds. Invalid or single-point ranges disable transport with explanatory text."
    },
    "value": {
      "type": "number",
      "required": true,
      "description": "Controlled cursor in Unix epoch milliseconds. Out-of-range values clamp for display and disable transport; invalid dates display an unknown position."
    },
    "playing": {
      "type": "boolean",
      "required": true,
      "description": "Caller-owned playback state. The component owns no clock and makes no optimistic state changes."
    },
    "onValueChange": {
      "type": "function",
      "required": true,
      "description": "(value: number) => void. Requests a valid in-range timestamp after requesting pause."
    },
    "onPlayingChange": {
      "type": "function",
      "required": true,
      "description": "(playing: boolean) => void. Requests play/pause. Scrubbing and event stepping call this with false before onValueChange."
    },
    "playbackRate": {
      "type": "number",
      "default": 1,
      "description": "Controlled positive finite speed multiplier. A valid supplied rate remains visible, in order, even when absent from playbackRates; invalid rates display Unknown rate."
    },
    "playbackRates": {
      "type": "array",
      "description": "Readonly number array of offered speed multipliers. Defaults to [0.5, 1, 2, 4]. Non-positive/non-finite rates and duplicates are omitted. Listed in ascending order."
    },
    "onPlaybackRateChange": {
      "type": "function",
      "description": "(rate: number) => void. Supplying this callback shows the controlled speed selector."
    },
    "eventTimes": {
      "type": "array",
      "description": "Readonly epoch-millisecond array. Supplying it shows previous/next event buttons. Times are sorted, deduplicated and filtered to valid dates within the range; stepping skips the current time."
    },
    "liveTime": {
      "type": "number",
      "description": "Application-supplied live reference in epoch milliseconds. May lie outside the review range; the application owns any range change."
    },
    "followingLive": {
      "type": "boolean",
      "default": false,
      "description": "Shows Following live and disables Return to live while the caller follows its reference."
    },
    "onReturnToLive": {
      "type": "function",
      "description": "() => void. Shows Return to live only when liveTime is a valid date. Requests no cursor, range or playing change itself."
    },
    "disabled": {
      "type": "boolean",
      "default": false,
      "description": "Disables all transport actions and removes them from sequential keyboard navigation."
    },
    "label": {
      "type": "string",
      "default": "Playback",
      "description": "Accessible root-group name. Also names the position slider and playback-speed selector."
    },
    "className": {
      "type": "string"
    }
  },
  "composable_with": [
    "MediaFrame",
    "TimeRange",
    "EventTimeline",
    "Chart"
  ],
  "never_inside": [
    "Button"
  ],
  "example": "<PlaybackControls start={start} end={end} value={cursor} playing={playing} onValueChange={setCursor} onPlayingChange={setPlaying} eventTimes={eventTimes} playbackRate={rate} onPlaybackRateChange={setRate} liveTime={liveTime} followingLive={followingLive} onReturnToLive={returnToLive} />"
}

On this page