Search Docs
Loading...
Skip to content

Markers and Frame Rate

Set the frame rate of a scene and name points and spans on its timeline with markers.

5 mins
estimated time
GitHub

The frame rate of a scene sets how many frames a second of its timeline has. Markers give names to points and spans on that timeline, such as chapters or beats, and travel with the scene. The editor has no controls for either, so you set them with the API.

This guide covers how to set the frame rate, how to add, read and remove markers, how to jump to a marker, and the errors to expect. It ends with a frame export.

Creating the Ball#

The example works on scene and on page, a page of 10 seconds. A small local function adds a ball that crosses the page in 10 seconds, so the playback time is easy to see.

// Creates a ball that crosses the page in 10 seconds
fun createBall() {
val ball = engine.block.create(DesignBlockType.Graphic)
engine.block.setShape(block = ball, shape = engine.block.createShape(ShapeType.Ellipse))
engine.block.setFill(block = ball, fill = engine.block.createFill(FillType.Color))
engine.block.setFillSolidColor(block = ball, color = Color.fromRGBA(r = 0.2F, g = 0.4F, b = 0.9F, a = 1F))
engine.block.setWidth(block = ball, value = 160F)
engine.block.setHeight(block = ball, value = 160F)
engine.block.setPositionY(block = ball, value = 460F)
engine.block.appendChild(parent = page, child = ball)
engine.block.setDuration(block = ball, duration = 10.0)
engine.block.setKeyframes(
block = ball,
property = "position/x",
keyframes = listOf(
Keyframe(time = 0F, value = KeyframeValue.Float(100F)),
Keyframe(time = 1F, value = KeyframeValue.Float(1660F)),
),
)
}

The example calls it once.

// A ball crosses the page over the 10 seconds of the page
createBall()

Setting the Frame Rate#

scene/frameRate is a number property of the scene, in frames per second. It is 30 by default and accepts values greater than 0 and up to 240. getFloat reads it and setFloat sets it. The preview motion blur uses it, and so does a video export that passes no frame rate of its own.

25.0
// 30 frames per second by default
val defaultFrameRate = engine.block.getFloat(block = scene, property = "scene/frameRate")
engine.block.setFloat(block = scene, property = "scene/frameRate", value = 25F)
val frameRate = engine.block.getFloat(block = scene, property = "scene/frameRate")
Log.i(TAG, "$defaultFrameRate, $frameRate")

Adding Markers#

Markers name points or spans on the scene timeline. engine.scene.addMarker appends a marker with a name, a start time and a duration in seconds. A duration of 0, the default, marks a point. Several markers may share a name.

// A marker has a name, a start time and a duration in seconds.
// A duration of 0 marks a point.
engine.scene.addMarker(name = "intro", time = 0.0, duration = 2.5)
engine.scene.addMarker(name = "beat", time = 4.0)
engine.scene.addMarker(name = "beat", time = 6.0)
engine.scene.addMarker(name = "outro", time = 8.0, duration = 2.0)

Reading Markers#

engine.scene.getMarkers returns the markers in the order they were added, as SceneMarker values with a name, a time and a duration.

val markers = engine.scene.getMarkers()
// [intro, beat, beat, outro]
val markerNames = markers.map { it.name }
Log.i(TAG, "$markerNames")

Jumping to a Marker#

Markers do not change the playback on their own. To jump to one, find it by name and pass its time to setPlaybackTime of the page.

// Jump to the first marker named "beat"
markers.firstOrNull { it.name == "beat" }?.let { beat ->
engine.block.setPlaybackTime(block = page, time = beat.time)
}
// 4.0
val playbackTimeAtBeat = engine.block.getPlaybackTime(block = page)
Log.i(TAG, "$playbackTimeAtBeat")

Removing a Marker#

engine.scene.removeMarker removes a marker by its index in the list that engine.scene.getMarkers returns. The markers after it move up by one.

// Remove the second "beat" by its index in the list
engine.scene.removeMarker(index = 2)
// 3
val markerCountAfterRemove = engine.scene.getMarkers().size
Log.i(TAG, "$markerCountAfterRemove")

Handling Errors#

A finite frame rate outside its range, a negative or non-finite marker time or duration, and an index outside the marker list are refused, and nothing changes. The call throws an EngineException with a code.

val frameRateErrorCode = try {
// A frame rate is greater than 0 and at most 240
engine.block.setFloat(block = scene, property = "scene/frameRate", value = 0F)
null
} catch (error: EngineException) {
// SCENE.FRAME_RATE_OUT_OF_RANGE
error.code
}
val markerTimeErrorCode = try {
engine.scene.addMarker(name = "before", time = -1.0)
null
} catch (error: EngineException) {
// SCENE.MARKER_INVALID_TIME
error.code
}
val markerIndexErrorCode = try {
engine.scene.removeMarker(index = 10)
null
} catch (error: EngineException) {
// SCENE.MARKER_INDEX_OUT_OF_RANGE
error.code
}

Exporting a Frame#

The playback still stands at the beat marker, so we export the page as PNG at 4 seconds, with the ball 40 percent of its way across the page.

// The playback time is at the "beat" marker, 4 seconds into the page
val previewPng = engine.block.export(block = page, mimeType = MimeType.PNG)
Log.i(TAG, "PNG bytes: ${previewPng.remaining()}")

Markers, Frame Rate and Other Operations#

  • Saving and loading keep the markers and the frame rate. Undo and redo restore edits to them.
  • A scene load drops markers with a negative time or duration, and loads a frame rate above 240 as 240.
  • A scene imported from a Lottie file on Web or Node carries the Lottie frame rate and markers, so they are already set when you load that scene.

Troubleshooting#

  • The frame rate is not a number (BLOCK.VALUE_NOT_FINITE): pass a finite value.
  • The frame rate is refused (SCENE.FRAME_RATE_OUT_OF_RANGE): pass a value greater than 0 and up to 240.
  • The marker is refused (SCENE.MARKER_INVALID_TIME): pass a time and a duration that are finite and not negative.
  • The marker cannot be removed (SCENE.MARKER_INDEX_OUT_OF_RANGE): pass an index from 0 to the number of markers minus 1.
  • The wrong marker is removed: indexes follow the order of the list, and removing a marker moves the later ones up. Read the markers again after you remove one.

API Reference#

Method Purpose
engine.block.getFloat(block=_, property="scene/frameRate") Read the frame rate of the scene
engine.block.setFloat(block=_, property="scene/frameRate", value=_) Set the frame rate, greater than 0 and up to 240
engine.scene.addMarker(name=_, time=_, duration=_) Add a marker; duration defaults to 0
engine.scene.getMarkers() Read the markers in the order they were added
engine.scene.removeMarker(index=_) Remove the marker at an index
engine.block.setPlaybackTime(block=_, time=_) Jump to a time, for example a marker’s
engine.block.getPlaybackTime(block=_) Read the playback time
engine.block.create(blockType=_) Create the ball
engine.block.createShape(type=_) Create the ellipse shape
engine.block.setShape(block=_, shape=_) Give the ball its shape
engine.block.createFill(fillType=_) Create a color fill
engine.block.setFill(block=_, fill=_) Give the ball its fill
engine.block.setFillSolidColor(block=_, color=_) Set the fill color
engine.block.setWidth(block=_, value=_) Set the width of the ball
engine.block.setHeight(block=_, value=_) Set the height of the ball
engine.block.setPositionY(block=_, value=_) Set the y position of the ball
engine.block.appendChild(parent=_, child=_) Add the ball to the page
engine.block.setDuration(block=_, duration=_) Set the duration of the ball
engine.block.setKeyframes(block=_, property=_, keyframes=_) Move the ball
engine.block.export(block=_, mimeType=_) Export the frame as PNG

Next Steps#

  • Scenes: the other properties and methods of a scene.
  • Motion Blur: blur moving blocks over one frame of the scene frame rate.