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.
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 |
// 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 playKilling 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(), 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 |
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