Skip to content

Timeline

The Timeline drives the composition. It owns all layers and clips, controls playback, and tracks the current frame. You access it through Engine.getInstance().getTimeline().

Playback

typescript
const timeline = Engine.getInstance().getTimeline();

await timeline.play();
timeline.pause();
timeline.stop(); // pauses and moves to time 0
timeline.seek(5.5); // jump to second 5.5

The Engine also exposes shortcuts for the same methods: Engine.getInstance().play(), pause(), stop(), and seek(time).

typescript
// Put two shapes on the timeline and start playback from second 1.
const first = await layer.addClip({
  type: "shape",
  shape: "circle",
  startTime: 0,
  duration: 3,
  style: { fillColor: "#6d6ff2", width: 400, height: 400 },
});
first.style.setPosition(960, 540);

const second = await layer.addClip({
  type: "shape",
  shape: "circle",
  startTime: 3,
  duration: 3,
  style: { fillColor: "#FF4D6D", width: 400, height: 400 },
});
second.style.setPosition(960, 540);

const timeline = Engine.getInstance().getTimeline();
timeline.seek(1);
await timeline.play();
Idle

INFO

seek aligns the requested time to the closest frame boundary based on the configured FPS. Use alignTime if you need manual control over the rounding direction.

Property Keyframes

Clips expose a propertyAnimator for keyframing individual clip properties over local clip time. Use it to animate properties such as position, scale, rotation, or opacity without configuring an animation preset.

typescript
// Add a rectangle to animate.
const clip = await layer.addClip({
  type: "shape",
  shape: "rectangle",
  startTime: 0,
  duration: 4,
  style: { fillColor: "#6d6ff2", width: 700, height: 400 },
});

clip.style.setPosition(960, 540);

// Add keyframes in local clip time.
// At second 0, the clip starts shifted left and transparent.
clip.propertyAnimator.addKeyframe("positionX", 0, 560);
clip.propertyAnimator.addKeyframe("alpha", 0, 0);

// At second 1, it reaches its final position and opacity.
clip.propertyAnimator.addKeyframe("positionX", 1, 960);
clip.propertyAnimator.addKeyframe("alpha", 1, 1);

// Add a subtle scale pulse between seconds 1 and 2.
clip.propertyAnimator.addKeyframe("scale", 1, [1, 1]);
clip.propertyAnimator.addKeyframe("scale", 1.5, [1.2, 1.2]);
clip.propertyAnimator.addKeyframe("scale", 2, [1, 1]);

const timeline = Engine.getInstance().getTimeline();
timeline.seek(0);
await timeline.play();
Idle

Keyframe times are relative to the clip, not the global timeline. For example, a keyframe at time: 1 plays one second after the clip starts.

Frames Per Second

typescript
timeline.setFps(30);
timeline.getFps();
timeline.getFrameDuration(); // returns 1 / fps

The default is 30. Changing the FPS reflows the internal time tracking to the new cadence.

Frame Alignment

The SDK works in seconds on the public API but internally snaps to frames to avoid floating-point drift between preview and render.

typescript
import { AlignRoundEnum } from "@rendley/sdk";

const aligned = timeline.alignTime(2.31, AlignRoundEnum.NEAREST); // CEIL | FLOOR | NEAREST
const frameNumber = timeline.getFrameNumberFromTime(2.31);
const frameNumbers = timeline.getFrameNumberFromTimeValues([1.5, 2.5, 3.5]);

Use getFrameNumberFromTimeValues when you have many time values to convert. It is faster than calling getFrameNumberFromTime in a loop.

Creating and Removing Layers

typescript
const layer = timeline.createLayer(); // append
const layer = timeline.createLayer({ index: 2 }); // insert at index

timeline.removeLayer(layer.id);

See Layer for layer-level controls.

Looking Up Clips and Layers

typescript
timeline.getLayerById(layerId);
timeline.getLayerByClipId(clipId);
timeline.getClipById(clipId);
timeline.getClipsByMediaId(mediaId); // all clips that reference this media
timeline.getClipsBySubtitlesId(subtitlesId);
timeline.getClips(); // every clip across layers

Adjust Layout

adjustClipsLayout walks every layer and pushes overlapping clips to the next available empty space. It is useful after programmatic changes (for example, after calling setPlaybackSpeed which can mutate clip duration):

typescript
timeline.adjustClipsLayout();

The method returns true if it had to move anything.

Splitting and Trimming Clips

Two common edits: split a clip into two pieces at a point in time, and trim the ends of a clip.

Split at Time

layer.splitClip(clipId, time) cuts the clip at a timeline time (not a source time). It keeps the left piece in place and returns the newly created right piece.

typescript
const clip = await layer.addClip({
  type: "shape",
  shape: "rectangle",
  startTime: 0,
  duration: 6,
  style: { fillColor: "#6d6ff2", width: 900, height: 500 },
});
clip.style.setPosition(960, 540);

// Cut at second 3, produces two clips on the same layer, butted end to end.
const rightHalf = await layer.splitClip(clip.getId(), 3);

// Recolor the right half so the split is visible.
if (rightHalf && rightHalf.getType() === "shape") {
  (rightHalf as any).style.setFillColor("#FF4D6D");
}
Idle

Trim the Ends

setLeftTrim(seconds) hides content from the start of the clip. setRightTrim(seconds) hides content from the end. Both are in seconds, and both clamp to the clip's real media duration.

typescript
clip.setLeftTrim(1.0); // skip the first second
clip.setRightTrim(0.5); // skip the last half second

const visible = clip.getTrimmedDuration(); // returns duration - leftTrim - rightTrim

For a video clip, trimming changes the visible range without re-encoding, playback and export seek into the source.

Trimming values are positive towards the inside the clip (both setLeftTrim(1) and setRightTrim(1) reduces the duration of the Clip by 1 second)

Moving Clips to Another Layer

typescript
layer.moveClipToLayer(clipId, destinationLayerId);

The destination layer reflows to fit (subject to adjustLayout). Useful for implementing drag-and-drop between tracks.

Duration

typescript
const end = timeline.getFitDuration(); // right-most clip end time across visible layers

Use this for trimming the preview bar or for configuring export ranges.

Volume

The timeline has a master volume that multiplies the volume of every audio and video clip:

typescript
timeline.setVolume(0.5);
timeline.getVolume();

Extract Audio

Take the audio track out of a video clip and get a standalone AudioClip on a new layer:

typescript
const audioClip = timeline.extractAudio(videoClip);

The original video clip is muted automatically. The returned audio clip is added to a new layer and can be moved, trimmed, or processed independently.

Subtitles Across Clips

Attach the same subtitles to multiple clips in a single call:

typescript
timeline.setSubtitles(subtitlesId, 0, [clipIdA, clipIdB]);

Omit the clipIds argument to apply subtitles to every clip in the timeline.

Partial Deserialization

loadSerializedData loads timeline state on top of the current Engine without resetting the rest of the project:

typescript
await timeline.loadSerializedData(serializedTimeline);

This is useful when you want to swap the composition but keep the Library, display, and settings intact.

Reset

To clear the timeline without affecting the Library or Display:

typescript
timeline.reset();

All layers, clips, and transitions are removed.