# Tween playback controls
Animate a signal and control the mounted playback with native buttons.

## APIs demonstrated

- [tween](/reference/functions/tween/) from `@pibbl/core`
- [drive](/reference/functions/drive/) from `@pibbl/core`
- [easing](/reference/functions/easing/) from `@pibbl/core`
- [usePlayback](/reference/hooks/use-playback/) from `@pibbl/core`
- [PibblEasing](/reference/types/canvas-runtime/) from `@pibbl/core`
- [PibblInterpolator](/reference/types/canvas-runtime/) from `@pibbl/core`
- [PibblTweenOptions](/reference/types/canvas-runtime/) from `@pibbl/core`
- [PibblAnimationDefinition](/reference/types/canvas-runtime/) from `@pibbl/core`
- [PibblPlayback](/reference/types/canvas-runtime/) from `@pibbl/core`
- [PibblPlaybackOptions](/reference/types/canvas-runtime/) from `@pibbl/core`
- [PibblPlaybackEvent](/reference/types/canvas-runtime/) from `@pibbl/core`
- [PibblPlaybackStatus](/reference/types/canvas-runtime/) from `@pibbl/core`

## Complete example

[Plain source](/minimal/animation/tween.tsx). Run as a browser module after the document body exists. TSX uses `jsx: "react-jsx"` and `jsxImportSource: "@pibbl/core"`.

```tsx
import {
  pibbl,
  Circle,
  Text,
  useSignal,
  usePlayback,
  drive,
  tween,
  easing,
  type PibblEasing,
  type PibblInterpolator,
  type PibblTweenOptions,
  type PibblAnimationDefinition,
  type PibblPlayback,
  type PibblPlaybackOptions,
  type PibblPlaybackEvent,
  type PibblPlaybackStatus,
} from "@pibbl/core";

const canvas = document.createElement("canvas");
canvas.width = 420;
canvas.height = 160;
document.body.append(canvas);
const events = document.createElement("output");
document.body.append(events);
const curve: PibblEasing = easing.easeInOut;
const interpolate: PibblInterpolator<number> = (from, to, progress) =>
  from + (to - from) * progress;
const options: PibblTweenOptions<number> = {
  from: 30,
  to: 360,
  duration: 2400,
  easing: curve,
  interpolate,
};
const definition: PibblAnimationDefinition<number> = tween(options);
const playbackOptions: PibblPlaybackOptions = {
  autoplay: true,
  onEvent(event: PibblPlaybackEvent) {
    events.textContent = event.type;
  },
};
let controls: PibblPlayback | undefined;
function Scene() {
  const x = useSignal(30);
  const playback = usePlayback(drive(x, definition), playbackOptions);
  controls = playback;
  const status: PibblPlaybackStatus = playback.status.get();
  return (
    <>
      <Circle style={{ radius: 20, left: x, top: 60, fill: "#087e8b" }} />
      <Text
        style={{ left: 20, top: 120, fill: "#222", font: "16px sans-serif" }}
      >
        {status}
      </Text>
    </>
  );
}
const controller = pibbl(canvas, <Scene />);
const commands: [string, () => void][] = [
  ["Replay", () => controls?.play()],
  ["Pause", () => controls?.pause()],
  ["Resume", () => controls?.resume()],
  ["Reverse", () => controls?.reverse()],
  ["Seek halfway", () => controls?.seek(1200)],
  ["Double speed", () => controls?.setPlaybackRate(2)],
  ["Finish", () => controls?.finish()],
  ["Cancel", () => controls?.cancel()],
];
for (const [label, action] of commands) {
  const button = document.createElement("button");
  button.textContent = label;
  button.onclick = action;
  document.body.append(button);
}
window.addEventListener("pagehide", () => controller.dispose(), { once: true });

```

## Usage notes

- Playback owns the animation; disposing the Pibbl mount releases it. Commands are applied by Pibbl's shared scheduler.

## Documentation version

Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.
