Timeline Control

Play, pause, reverse, seek, and inspect timeline state programmatically.

Every Motion timeline is a named, registry-backed instance you can retrieve and control from anywhere — event handlers, framework lifecycle hooks, or user interactions.


Retrieving a Timeline

Use the single-argument overload to get or lazily create a timeline by name. If the name is already registered, it returns that same Timeline instance. If the name is missing, it registers a new inactive, duration-zero timeline — useful for trigger-only timelines such as custom cursors or pin-only scroll scenes.

typescript
import { Motion } from "@motion.page/sdk";

// Create a timeline on page load, paused
Motion("hero", "#hero", { from: { opacity: 0, y: 40 }, duration: 0.8 })
  .onPageLoad({ paused: true });

// Later — retrieve by name and play manually
const tl = Motion("hero");
tl.play();

The static helpers Motion.get() and Motion.has() are safer for conditional retrieval:

Method Returns Description
Motion("name") Timeline Get an existing timeline, or create and register an empty one if missing
Motion.get("name") Timeline | undefined Safe retrieval — returns undefined if not found
Motion.has("name") boolean Check if a timeline is registered
Motion.getNames() string[] All registered timeline names
typescript
// Guard before accessing
if (Motion.has("hero")) {
  Motion.get("hero")?.pause();
}

Playback Methods

All playback methods return this for chaining.

Method Signature Description
.play() play(from?: number): this Play forward. Optional from jumps to that time in seconds first
.pause() pause(atTime?: number): this Pause. Optional atTime seeks to that time before pausing
.reverse() reverse(from?: number): this Play backward from the current position. Optional from jumps first
.restart() restart(): this Reset to time 0 and play forward
.seek() seek(position: number): this Jump to a time in seconds without changing play state
.kill() kill(clearProps?: boolean): void Destroy the timeline. clearProps: true (default) restores CSS
.clear() clear(): this Remove all animations without destroying the timeline instance
typescript
import { Motion } from "@motion.page/sdk";

Motion("banner", "#banner", { from: { opacity: 0, y: 30 }, duration: 1 })
  .onPageLoad({ paused: true });

const tl = Motion("banner");

tl.play();          // play from current position
tl.pause();         // freeze
tl.seek(0.5);       // jump to 0.5s, stay paused
tl.reverse();       // play backward from current position
tl.restart();       // reset to 0 and play

Killing a Timeline

.kill() removes the timeline from the registry and — by default — restores all animated CSS properties to their pre-animation values.

typescript
// Kill and restore CSS (default)
Motion("hero").kill();

// Kill but leave element in its current animated state
Motion("hero").kill(false);

After .kill(), Motion.has("hero") is false and Motion.get("hero") returns undefined. Calling Motion("hero") again creates and registers a fresh empty timeline; pass a target and config when you want to rebuild the animation immediately.


State Inspection

Read timeline state at any point. Methods marked as getter/setter accept an optional value — call with no argument to read, with an argument to set and return this.

Method Signature Description
.isActive() isActive(): boolean true if the timeline is currently animating
.progress() progress(value?: number): number | this Progress as 0–1. Pass a value to seek by progress
.time() time(value?: number): number | this Elapsed time in seconds. Pass a value to seek to that time
.duration() duration(): number Total timeline duration in seconds (read-only)
.timeScale() timeScale(value?: number): number | this Playback speed multiplier. 1 = normal, 2 = double, 0.5 = half
.getName() getName(): string | undefined The timeline’s registered name
typescript
const tl = Motion("sequence");

// Read current state
console.log(tl.isActive());   // true | false
console.log(tl.progress());   // 0–1
console.log(tl.time());       // seconds elapsed
console.log(tl.duration());   // total seconds
console.log(tl.timeScale());  // 1 (normal speed)
console.log(tl.getName());    // "sequence"

// Seek to 50% progress
tl.progress(0.5);

// Jump to 2 seconds
tl.time(2);

// Slow down to half speed
tl.timeScale(0.5);

Callbacks

Timeline-Level Callbacks

Chain callback methods on the Timeline instance to react to lifecycle events. These are separate from the trigger — chain them before or after:

Method Signature Description
.onStart(cb) onStart(cb: () => void): this Fires when the timeline begins playing
.onUpdate(cb) onUpdate(cb: (progress: number, time: number) => void): this Fires every frame with current progress (0–1) and time (seconds)
.onComplete(cb) onComplete(cb: () => void): this Fires when the timeline finishes
.call(cb, params?, position?) call(cb, params?, position?): this Insert a function call at a specific position in the timeline
typescript
import { Motion } from "@motion.page/sdk";

Motion("reveal", ".hero", { from: { opacity: 0, y: 40 }, duration: 0.8 })
  .onStart(() => {
    console.log("Animation started");
  })
  .onUpdate((progress, time) => {
    console.log(`Progress: ${(progress * 100).toFixed(1)}%  Time: ${time.toFixed(2)}s`);
  })
  .onComplete(() => {
    console.log("Animation complete");
  })
  .onPageLoad();

Use .call() to trigger a function at a precise moment within a multi-step timeline:

typescript
Motion("sequence", [
  { target: "#title",    from: { opacity: 0 }, duration: 0.5 },
  { target: "#subtitle", from: { opacity: 0 }, duration: 0.4, position: "+=0.1" },
])
  .call(() => console.log("Title appeared"), [], 0.5)
  .call(() => console.log("Sequence complete"), [], ">")
  .onPageLoad();

Per-Animation Callbacks

Individual animations inside a timeline also support callbacks via AnimationConfig. These fire relative to each animation rather than the timeline as a whole.

Property Type Description
onStart () => void Fires when this animation begins
onUpdate (progress: number) => void Fires every frame for this animation
onComplete () => void Fires when this animation finishes
onRepeat (repeatCount: number) => void Fires at the start of each repeat cycle
onReverseComplete () => void Fires when this animation completes in reverse
typescript
Motion("loader", ".bar", {
  to: { width: "100%" },
  duration: 2,
  ease: "power1.inOut",
  onUpdate: (progress) => {
    document.querySelector<HTMLElement>("#label")!.textContent =
      `${Math.round(progress * 100)}%`;
  },
  onComplete: () => {
    document.querySelector<HTMLElement>("#label")!.textContent = "Done";
  },
}).play();


Common Patterns

Play/Pause Toggle

typescript
import { Motion } from "@motion.page/sdk";

Motion("panel", "#panel", { from: { opacity: 0, y: 20 }, duration: 0.8 })
  .onPageLoad({ paused: true });

document.querySelector("#toggle")?.addEventListener("click", () => {
  const tl = Motion("panel");
  tl.isActive() ? tl.pause() : tl.play();
});

Progress Bar Driven by Timeline

typescript
import { Motion } from "@motion.page/sdk";

const bar = document.querySelector<HTMLElement>("#progress-bar")!;

Motion("hero-sequence", [
  { target: "#title",    from: { opacity: 0, y: 30 }, duration: 0.6 },
  { target: "#subtitle", from: { opacity: 0 },         duration: 0.5, position: "+=0.1" },
  { target: "#cta",      from: { opacity: 0, y: 10 }, duration: 0.4, position: "+=0.1" },
])
  .onUpdate((progress) => {
    bar.style.width = `${progress * 100}%`;
  })
  .onPageLoad();

Scrubbing with seek()

Bind .seek() to a range input to let users scrub through a timeline manually:

typescript
import { Motion } from "@motion.page/sdk";

Motion("scrub-demo", "#card", {
  from: { x: -200, opacity: 0 },
  to:   { x:  200, opacity: 1 },
  duration: 2,
}).onPageLoad({ paused: true });

document.querySelector<HTMLInputElement>("#scrubber")?.addEventListener("input", (e) => {
  const input = e.target as HTMLInputElement;
  const tl = Motion("scrub-demo");
  tl.seek((Number(input.value) / 100) * tl.duration());
});

Slowing Down or Speeding Up

timeScale adjusts playback speed without changing any configured durations:

typescript
import { Motion } from "@motion.page/sdk";

Motion("entrance", ".modal", { from: { opacity: 0, scale: 0.92 }, duration: 0.6 })
  .onPageLoad({ paused: true });

// Slow motion preview
Motion("entrance").timeScale(0.25).play();

// Normal speed
Motion("entrance").timeScale(1).restart();

Stopping a Repeating Animation

Use onRepeat on a per-animation basis to stop after a set number of observed cycles:

typescript
import { Motion } from "@motion.page/sdk";

let cycles = 0;

Motion("pulse", ".badge", {
  to: { scale: 1.2 },
  duration: 0.4,
  ease: "power1.inOut",
  repeat: { times: -1, yoyo: true },
  onRepeat: () => {
    cycles++;
    if (cycles >= 6) Motion("pulse").kill();
  },
}).play();

Related: Core Concepts · Duration & Delay