Playback

A clip's transport on ONE row: play or pause, a scrub bar you can drag to any moment, and the playback speed. A Widget3d, so it sits in a panel3d beside the rest of a panel and works the same flat, in a scene and in a headset.

Demo

import { b3d, b3dLight, panelScene, panel3d, label3d, playback3d } from 'tosijs-3d'
import { elements } from 'tosijs'
const { div, pre } = elements

// A pretend clip, four seconds long, run by the page.
const clip = { time: 0, duration: 4, playing: true, speed: 1 }
const readout = pre({ style: 'margin:8px 16px;color:#8ea;font:12px ui-monospace,monospace' }, '')
setInterval(() => {
  if (clip.playing) clip.time = (clip.time + 0.05 * clip.speed) % clip.duration
  readout.textContent = `${clip.time.toFixed(2)} s of ${clip.duration} s  ·  ${clip.playing ? 'playing' : 'paused'}  ·  ×${clip.speed}`
}, 50)

const panel = panel3d(
  { width: 320 },
  label3d({ text: 'Walk_Loop', muted: true, compact: true }),
  playback3d({
    read: () => clip,
    handlePlay: (on) => { clip.playing = on },
    handleSeek: (t) => { clip.time = t },
    handleSpeed: (s) => { clip.speed = s },
  })
)

const { plane, sceneCreated } = panelScene({ svg: panel, target: panel, width: 2.4, camera: { radius: 3.2 } })
const scene = b3d({ style: 'border-radius:8px;overflow:hidden', sceneCreated }, b3dLight({ intensity: 1 }), plane)

preview.append(
  div(
    { style: 'display:flex;flex-direction:column;height:100%;background:#0c0e14' },
    div(
      { style: 'display:flex;gap:24px;flex:1;min-height:0;padding:16px 16px 4px' },
      div({ style: 'color:#9ab;font:12px system-ui;display:flex;flex-direction:column;gap:6px;flex:1;min-width:0' }, 'DOM', panel),
      div({ style: 'color:#9ab;font:12px system-ui;display:flex;flex-direction:column;gap:6px;flex:1;min-width:0' }, '3D texture', scene)
    ),
    readout
  )
)
.preview {
  height: 100%;
}

Press the button to pause, drag the bar to any moment (it pauses there, so you can look), and pick a speed from the menu.

The clip is the host's

The widget holds no clock. It asks read() what the clip is doing (its time and duration in seconds, whether it is playing, its speed) a few times a second while it is on screen, and reports what a hand does through handlePlay, handleSeek and handleSpeed. So it follows a clip however the clip is driven, and a pause in the scene shows in the button.

Scrubbing pauses the clip, since a moment you drag to is one you want to see held. Press play to carry on from there.

speeds sets the menu (default 0.1, 0.25, 0.5, 0.75, 1, 1.5 and 2).