---
title: "Timing & Animation"
description: "Set clip timing, elastic holds, GSAP tweens, and keyframed animations on composition elements."
---

Every clip in a HyperFrames composition has a position on the timeline (`data-start`, `data-duration`) and an optional animation attached to it. The SDK exposes typed helpers for both: the timing API controls *when* an element appears and how long it stays on screen; the animation API controls *how* it moves through that window.

## Clip Timing

### Reading timings

`getElementTimings()` returns a `Record<HfId, ElementTimingSnapshot>` — one entry per element that carries timing data. Values are derived from the live DOM on every call (never cached in the snapshot). Keys are `scopedId`: for top-level elements this equals the bare `id`, but for elements inside a sub-composition it is the scoped form `"hf-HOST/hf-LEAF"`.

```typescript
import { openComposition } from "@hyperframes/sdk";

const comp = await openComposition(html);
const timings = comp.getElementTimings();

for (const [id, t] of Object.entries(timings)) {
  console.log(id, t.enterAt, "→", t.exitAt, "labels:", t.labels);
}
```

Each `ElementTimingSnapshot` contains:

<ResponseField name="enterAt" type="number">
  Absolute timeline position (seconds) at which the element enters.
</ResponseField>
<ResponseField name="exitAt" type="number">
  Absolute timeline position (seconds) at which the element exits.
</ResponseField>
<ResponseField name="labels" type="string[]">
  GSAP timeline label names whose numeric position falls within `[enterAt, exitAt]`. Parsed fresh from the GSAP script on every `getElementTimings()` call — never stale.
</ResponseField>

<Note>
  `getElementTimings()` only includes elements that have `data-start` and either `data-duration` or `data-end` attributes. Untimed elements are omitted. The method prefers `data-duration` over `data-end − data-start` when both exist, matching the behavior of `setTiming`.
</Note>

### Setting timing on one element

```typescript
// Move hf-title to start at 1.0 s, last 2.5 s, on track 0
comp.setTiming("hf-title", { start: 1.0, duration: 2.5, trackIndex: 0 });
```

All three fields are optional — pass only what you want to change:

<ParamField path="start" type="number">
  Clip start time in seconds.
</ParamField>
<ParamField path="duration" type="number">
  Clip duration in seconds.
</ParamField>
<ParamField path="trackIndex" type="number">
  Zero-based track index.
</ParamField>

### Updating multiple elements in one batch

`setElementTiming(map)` dispatches one `setTiming` op per entry inside a single batch, so the history records one undo step and patch listeners see one event:

```typescript
comp.setElementTiming({
  "hf-title": { start: 0.5, duration: 2.0 },
  "hf-logo":  { start: 0.0, duration: 5.0, trackIndex: 1 },
  "hf-cta":   { start: 3.0, duration: 2.5 },
});
```

Unknown ids are silently skipped.

### Elastic holds

An elastic hold freezes or loops a portion of an element's timeline window. Use it to hold a static frame between two animated segments without changing clip duration.

```typescript
comp.setHold("hf-card", {
  start: 1.5,   // hold begins at this time within the composition
  end: 4.0,     // hold ends at this time
  fill: "freeze",  // "freeze" | "loop"
});
```

`ElasticHold` fields:

<ParamField path="start" type="number">
  Absolute composition time at which the hold begins.
</ParamField>
<ParamField path="end" type="number">
  Absolute composition time at which the hold ends.
</ParamField>
<ParamField path="fill" type='"freeze" | "loop"'>
  `"freeze"` holds the last frame until `end`; `"loop"` repeats the segment from `start` back to `start`.
</ParamField>

---

## GSAP Tweens

GSAP tweens are the primary animation primitive. The SDK reads tween IDs from `element.animationIds` (returned by the query API) and writes through `addGsapTween`, `setGsapTween`, and `removeGsapTween`.

### Adding a tween

```typescript
const animId = comp.addGsapTween("hf-title", {
  method: "from",
  position: 0.5,
  duration: 0.6,
  ease: "power3.out",
  fromProperties: { opacity: 0, y: 40 },
});
// animId is the newly-assigned animation ID — store it for subsequent edits
```

`addGsapTween` returns the newly-assigned animation ID as a `string`.

### GsapTweenSpec fields

<ParamField path="method" type='"from" | "to" | "fromTo" | "set"'>
  The GSAP timeline method to call.
</ParamField>
<ParamField path="position" type="number | string">
  Timeline position: a number (seconds) or a label-relative string (e.g. `"intro+=0.3"`). Number-only is required for `addWithKeyframes` / `replaceWithKeyframes` — see [Keyframes](#keyframes) below.
</ParamField>
<ParamField path="duration" type="number">
  Tween duration in seconds.
</ParamField>
<ParamField path="ease" type="string">
  GSAP ease string, e.g. `"power2.inOut"`.
</ParamField>
<ParamField path="fromProperties" type="Record<string, unknown>">
  Starting properties — used with `"from"` and `"fromTo"`.
</ParamField>
<ParamField path="toProperties" type="Record<string, unknown>">
  Ending properties — used with `"fromTo"`.
</ParamField>
<ParamField path="properties" type="Record<string, unknown>">
  Animation target properties — used with `"to"` tweens.
</ParamField>
<ParamField path="repeat" type="number">
  Number of repeats (`-1` = infinite). Maps to GSAP's `repeat` option.
</ParamField>
<ParamField path="yoyo" type="boolean">
  When `true`, alternates direction on each repeat.
</ParamField>
<ParamField path="stagger" type="number | Record<string, unknown>">
  GSAP stagger amount (seconds) or a full stagger config object.
</ParamField>

### Modifying a tween

```typescript
// Change ease and duration — all other fields stay as-is
comp.setGsapTween(animId, { ease: "elastic.out(1, 0.5)", duration: 0.9 });
```

`setGsapTween` takes the animation ID and a `Partial<GsapTweenSpec>` — only the keys you pass are updated.

### Removing a tween

```typescript
comp.removeGsapTween(animId);
```

### Looking up animation IDs

Query the element snapshot to find animation IDs attached to a given element:

```typescript
const el = comp.getElement("hf-title");
const [firstAnimId] = el?.animationIds ?? [];
if (firstAnimId) {
  comp.setGsapTween(firstAnimId, { ease: "none" });
}
```

### Feature-detecting advanced ops

Some GSAP operations (such as `setGsapKeyframe`, arc paths, and label ops) require the parser engine, which ships in a later phase. Use `can()` to gate them before dispatching:

```typescript
const check = comp.can({
  type: "setGsapTween",
  animationId: firstAnimId,
  properties: { ease: "power3.inOut" },
});

if (!check.ok) {
  if (check.code === "E_NO_GSAP_TIMELINE") {
    // Parser engine not yet available — show a disabled state or skip
    console.warn("GSAP timeline not available:", check.message);
  }
  return;
}

comp.dispatch({ type: "setGsapTween", animationId: firstAnimId, properties: { ease: "power3.inOut" } });
```

Stable error codes returned by `can()`:

| Code | Meaning |
|------|---------|
| `E_TARGET_NOT_FOUND` | The target `HfId` does not exist in the document. |
| `E_NO_ROOT` | The document has no root element. |
| `E_NO_GSAP_TIMELINE` | Op requires the GSAP parser engine, which is not yet available. |
| `E_NO_GSAP_SCRIPT` | The composition has no embedded GSAP script. |

---

## Keyframes

For keyframe-based animations, use `addWithKeyframes` and `replaceWithKeyframes`. Both operate on the GSAP CSS-keyframes layer and return the minted animation ID.

### Adding a keyframed tween

```typescript
const animId = comp.addWithKeyframes(
  "#hf-badge",   // CSS selector targeting the element
  1.0,           // timeline position in seconds (number only)
  0.8,           // duration in seconds
  [
    { percentage: 0,   properties: { opacity: 0, scale: 0.8 } },
    { percentage: 60,  properties: { opacity: 1, scale: 1.05 }, ease: "power2.out" },
    { percentage: 100, properties: { scale: 1 } },
  ],
  "power2.inOut", // optional overall ease
);
```

Returns the new animation ID string, or `""` if the op was rejected.

### Replacing an existing keyframed tween

```typescript
const newAnimId = comp.replaceWithKeyframes(
  oldAnimId,
  "#hf-badge",
  1.0,
  1.2,
  [
    { percentage: 0,   properties: { x: -60, opacity: 0 } },
    { percentage: 100, properties: { x: 0, opacity: 1 }, ease: "back.out(1.7)" },
  ],
);
// newAnimId !== oldAnimId — position-derived IDs renumber after the remove
```

<Warning>
  `replaceWithKeyframes` is equivalent to `removeGsapTween` + `addWithKeyframes` in one atomic op. Because position-derived tween IDs renumber after the removal step, the returned ID is always a **new** ID and must not be assumed equal to the input `animationId`. Re-query `element.animationIds` after a replace to get the current set.
</Warning>

### KeyframeSpec fields

<ParamField path="percentage" type="number">
  Position within the tween as a percentage (0–100).
</ParamField>
<ParamField path="properties" type="Record<string, number | string>">
  CSS / GSAP properties at this keyframe.
</ParamField>
<ParamField path="ease" type="string">
  Per-keyframe ease applied *from* this keyframe to the next.
</ParamField>
<ParamField path="auto" type="boolean">
  GSAP endpoint flag — when `true`, this keyframe picks up the element's current value automatically.
</ParamField>

### Lower-level keyframe and label ops

For lower-level operations — individual keyframe mutations (`addGsapKeyframe`, `setGsapKeyframe`, `removeGsapKeyframe`), property removal, arc paths, label insertion, and animation splitting — use `dispatch()` directly with the corresponding `EditOp` types. See the [Edit Operations reference](/sdk/reference/edit-operations) for the full op union.

---

<CardGroup cols={2}>
  <Card title="Edit Operations" icon="code" href="/sdk/reference/edit-operations">
    Full reference for every op type in the `EditOp` union, including lower-level keyframe and arc ops.
  </Card>
  <Card title="Types" icon="brackets-curly" href="/sdk/reference/types">
    `GsapTweenSpec`, `KeyframeSpec`, `ElasticHold`, `ElementTimingSnapshot`, and all related types.
  </Card>
  <Card title="GSAP Animation Guide" icon="wand-magic-sparkles" href="/guides/gsap-animation">
    Authoring GSAP timelines in composition HTML.
  </Card>
  <Card title="Keyframes Guide" icon="film" href="/guides/keyframes">
    Keyframe authoring patterns and best practices.
  </Card>
</CardGroup>

