Search Docs
Loading...
Skip to content

Motion Blur

Blur moving blocks in exported videos and in the preview, as a camera shutter does.

6 mins
estimated time
GitHub

Motion blur smears a moving block along its path during one frame. A block with motion blur draws the average of several samples over the time that the shutter stays open. The blur renders in video export and in the preview. The editor has no motion blur control, so you set it with the block API.

This guide covers how to turn motion blur on for a block, how to tune the shutter and the samples of the scene, how the frame rate sets the length of the blur, the preview blur and its setting, and video export.

Animating the Block#

Motion blur needs motion. A block blurs only while something moves it during the shutter: keyframes, a preset animation, the motion of its group, or a Lottie animation in its video fill. A block that stands still draws unchanged.

We move a square across the page in 1 second with position/x keyframes, and back. For keyframes, see Keyframes.

// A square that crosses the page in one second. Motion blur needs motion.
val block = engine.block.create(DesignBlockType.Graphic)
engine.block.setShape(block = block, shape = engine.block.createShape(ShapeType.Rect))
val fill = engine.block.createFill(FillType.Color)
engine.block.setColor(
block = fill,
property = "fill/color/value",
value = Color.fromRGBA(r = 0.9F, g = 0.2F, b = 0.4F, a = 1F),
)
engine.block.setFill(block = block, fill = fill)
engine.block.setWidth(block = block, value = 300F)
engine.block.setHeight(block = block, value = 300F)
engine.block.setPositionY(block = block, value = 390F)
engine.block.appendChild(parent = page, child = block)
engine.block.setDuration(block = block, duration = 2.0)
engine.block.setKeyframes(
block = block,
property = "position/x",
keyframes = listOf(
Keyframe(time = 0F, value = KeyframeValue.Float(100F)),
Keyframe(time = 0.5F, value = KeyframeValue.Float(1520F)),
Keyframe(time = 1F, value = KeyframeValue.Float(100F)),
),
)

Turning Motion Blur On#

The block property motionBlur/enabled turns motion blur on for one block. It is false by default. The setter needs the appearance/animation scope.

// Blur the block while it moves
engine.block.setBoolean(block = block, property = "motionBlur/enabled", value = true)
val blockMotionBlurEnabled = engine.block.getBoolean(block = block, property = "motionBlur/enabled")

Graphic, text and caption blocks have the property. Pages, scenes, groups and fills do not, and setting it on them throws. Use findAllProperties to check a block.

// Graphic, text and caption blocks have the property. Pages do not.
val blockHasMotionBlur = "motionBlur/enabled" in engine.block.findAllProperties(block = block)
val pageHasMotionBlur = "motionBlur/enabled" in engine.block.findAllProperties(block = page)
Log.i(TAG, "block: $blockHasMotionBlur, page: $pageHasMotionBlur")

A group does not blur as a whole. Turn motion blur on for its children, and they blur when the group moves.

Shutter Angle and Phase#

The scene holds one shutter for all blocks with motion blur. Both values are in degrees of one frame.

  • motionBlur/shutterAngle sets how long the shutter stays open. The default is 180, which is half a frame. The range is 0 to 720. A larger angle draws a longer trail, and 0 draws no blur.
  • motionBlur/shutterPhase sets where the shutter opens, relative to the frame time. The default is 0, so the trail lies ahead of the block. The range is -360 to 360. A phase of minus half the angle centers the blur on the frame time. A phase of minus the angle puts the trail behind the block.
// The scene holds the shutter for all blocks with motion blur.
// 360 degrees keeps the shutter open for one full frame.
engine.block.setFloat(block = scene, property = "motionBlur/shutterAngle", value = 360F)
// -180 degrees, half the angle, centers the shutter on the frame time
engine.block.setFloat(block = scene, property = "motionBlur/shutterPhase", value = -180F)

Samples#

motionBlur/samples sets how many samples one frame averages. The default is 16 and the range is 1 to 64. More samples give a smoother trail and take longer to render. With 1 sample, no blur is drawn.

// More samples give a smoother trail and take longer to render
engine.block.setInt(block = scene, property = "motionBlur/samples", value = 32)

Frame Rate#

The shutter angle refers to one frame of the video export. A video export without a frame rate option uses the scene property scene/frameRate, which is 30 by default. The preview uses the same frame. The frameRate option of ExportVideoOptions changes the length of one frame, so a higher frame rate draws a shorter trail.

// One frame of the scene frame rate is the span the shutter angle refers to
engine.block.setFloat(block = scene, property = "scene/frameRate", value = 30F)

Preview Motion Blur#

The preview draws the same blur as the video export. While the preview plays or changes, it draws fewer samples, at least 4, and smaller blur images to keep up. On a display that refreshes slower than the scene frame rate, it keeps up at the refresh rate of the display. It draws all samples once it settles. A block in crop or text editing draws sharp, so the edit shows.

The features/previewMotionBlurEnabled setting is true by default. Set it to false to show the blocks sharp while editing. The video export ignores this setting, and the scene does not save it. See Settings for the other editor settings.

// The preview draws the motion blur by default.
// Set the value to false to show the blocks sharp while editing.
engine.editor.setSettingBoolean(keypath = "features/previewMotionBlurEnabled", value = false)
val previewMotionBlurEnabled = engine.editor.getSettingBoolean(keypath = "features/previewMotionBlurEnabled")

Handling Errors#

A shutter or samples value outside its range throws an EngineException with the code BLOCK.MOTION_BLUR_VALUE_OUT_OF_RANGE. The scene keeps the old value.

val errorCode = try {
engine.block.setInt(block = scene, property = "motionBlur/samples", value = 100)
null
} catch (error: EngineException) {
// BLOCK.MOTION_BLUR_VALUE_OUT_OF_RANGE
error.code
}

Exporting a Video#

exportVideo draws the motion blur into every frame. An image export with export draws the blocks sharp, so a single frame never shows the blur.

// Image export draws no motion blur. Export a video to see it in a file.
val video = engine.block.exportVideo(
block = page,
timeOffset = 0.0,
duration = engine.block.getDuration(block = page),
mimeType = MimeType.MP4,
progressCallback = {},
)

Motion Blur and Other Operations#

  • Saving and loading a scene keeps the block flag and the shutter values.
  • Older scenes load with motion blur off and the default shutter.
  • Loaded values outside their range are clamped to the range.
  • Undo and redo restore the block flag and the shutter values.

Troubleshooting#

  • The block does not blur: check that something moves the block during the shutter, that motionBlur/samples is greater than 1 and that motionBlur/shutterAngle is greater than 0.
  • An exported image is sharp: image export draws no motion blur. Export a video.
  • The trail is too short: raise motionBlur/shutterAngle, or export with a lower frame rate.
  • The trail looks like separate copies: raise motionBlur/samples.
  • A value is out of range (BLOCK.MOTION_BLUR_VALUE_OUT_OF_RANGE): keep the angle from 0 to 720, the phase from -360 to 360 and the samples from 1 to 64.
  • A value is not a number (EDITOR.VALUE_NOT_FINITE): pass a finite shutter angle and phase.
  • The video export fails (ENCODE.VIDEO_BLOCK_HAS_ERROR): the trail of a block is too large to render on this device. Make the block or its motion smaller, or lower the export size.
  • Setting the flag throws: the block is not a graphic, text or caption block, or the appearance/animation scope is off.

API Reference#

Method Description
engine.block.setBoolean(block=_, property=_, value=_) Turn motionBlur/enabled on or off for a block.
engine.block.getBoolean(block=_, property=_) Check whether a block has motion blur.
engine.block.findAllProperties(block=_) List the properties of a block, to check for motionBlur/enabled.
engine.block.setFloat(block=_, property=_, value=_) Set motionBlur/shutterAngle, motionBlur/shutterPhase or scene/frameRate.
engine.block.getFloat(block=_, property=_) Read the shutter values.
engine.block.setInt(block=_, property=_, value=_) Set motionBlur/samples.
engine.block.getInt(block=_, property=_) Read the samples.
engine.editor.setSettingBoolean(keypath=_, value=_) Turn the preview motion blur on or off.
engine.editor.getSettingBoolean(keypath=_) Read the preview motion blur setting.
engine.block.setKeyframes(block=_, property=_, keyframes=_) Move the block.
engine.block.create(blockType=_) Create the page and the graphic block.
engine.block.createShape(type=_) Create the rect shape.
engine.block.createFill(fillType=_) Create the color fill.
engine.block.setColor(block=_, property=_, value=_) Set the fill color.
engine.block.setShape(block=_, shape=_) Attach the shape to the graphic block.
engine.block.setFill(block=_, fill=_) Attach the fill to the graphic block.
engine.block.setWidth(block=_, value=_) Set the block width.
engine.block.setHeight(block=_, value=_) Set the block height.
engine.block.setPositionY(block=_, value=_) Set the y position.
engine.block.appendChild(parent=_, child=_) Add the block to the page.
engine.block.setDuration(block=_, duration=_) Set the page and block duration.
engine.block.getDuration(block=_) Read the page duration for the export.
engine.block.exportVideo(block=_, timeOffset=_, duration=_, mimeType=_, progressCallback=_) Export a video with motion blur.

Next Steps#