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 an existing timeline by name. This returns the same Timeline instance that was created when the animation was first registered.
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 | Retrieve a registered timeline — throws if not found |
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 |
// 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 |
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.
// Kill and restore CSS (default)
Motion("hero").kill();
// Kill but leave element in its current animated state
Motion("hero").kill(false); After .kill(), calling Motion("hero") will throw. Use Motion.has("hero") to guard, or Motion.get("hero") which returns undefined safely.
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 |
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 |
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:
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 |
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
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
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:
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:
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:
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