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
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.5The Engine also exposes shortcuts for the same methods: Engine.getInstance().play(), pause(), stop(), and seek(time).
// 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();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.
// 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();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
timeline.setFps(30);
timeline.getFps();
timeline.getFrameDuration(); // returns 1 / fpsThe 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.
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
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
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 layersAdjust 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):
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.
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");
}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.
clip.setLeftTrim(1.0); // skip the first second
clip.setRightTrim(0.5); // skip the last half second
const visible = clip.getTrimmedDuration(); // returns duration - leftTrim - rightTrimFor 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
layer.moveClipToLayer(clipId, destinationLayerId);The destination layer reflows to fit (subject to adjustLayout). Useful for implementing drag-and-drop between tracks.
Duration
const end = timeline.getFitDuration(); // right-most clip end time across visible layersUse 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:
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:
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:
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:
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:
timeline.reset();All layers, clips, and transitions are removed.