Video fills let you fill shapes and graphics with video content. Unlike video blocks which represent standalone video elements, video fills apply video as a visual property to existing blocks — similar to how you might fill a shape with an image or color.
Context: Video Fills vs Video Blocks#
Video fills apply video as a fill property to graphic blocks. This is useful when you want to use video as a texture within shapes, mask video to specific areas, or create video-filled design elements. Video fills attach to graphic blocks via the fill API.
Video blocks are standalone elements that represent video content directly on the canvas. They’re created with engine.block.create('video') and represent complete video clips.
Reference Example#
The following code demonstrates the complete video fills API. Below, each section is explained in detail.
Understanding Video Fills#
Video fills require a video scene. The scene must be in Video mode for video fills to work correctly — Design mode does not support video fills.
// Create a video scene (required for video fills)// Video fills only work in Video mode, not Design modeengine.scene.createVideo({ page: { size: { width: 800, height: 600 } }});const page = engine.block.findByType('page')[0];Before working with fills, verify that the block supports fills and that the scene is in the correct mode.
// Check if block supports fills before accessing fill APIsconst testBlock = engine.block.create('graphic');const canHaveFill = engine.block.supportsFill(testBlock);console.log('Block supports fills:', canHaveFill);
// Verify we're in Video mode (required for video fills)const sceneMode = engine.scene.getMode();console.log('Scene mode:', sceneMode); // "Video"if (sceneMode !== 'Video') { throw new Error('Video fills require Video mode.');}engine.block.destroy(testBlock);Creating a Video Fill#
Create a video fill and apply it to a graphic block:
// Create a graphic block with a video fillconst basicBlock = engine.block.create('graphic');engine.block.setShape(basicBlock, engine.block.createShape('rect'));engine.block.setWidth(basicBlock, 200);engine.block.setHeight(basicBlock, 150);
// Create a video fillconst videoFill = engine.block.createFill('video');// or using full type name: engine.block.createFill('//ly.img.ubq/fill/video');
// Set the video source URIengine.block.setString(videoFill, 'fill/video/fileURI', videoUri);
// Apply the fill to the blockengine.block.setFill(basicBlock, videoFill);engine.block.appendChild(page, basicBlock);Retrieve the fill from a block and verify its type:
// Get and verify the current fillconst fillId = engine.block.getFill(basicBlock);const fillType = engine.block.getType(fillId);console.log('Fill type:', fillType); // '//ly.img.ubq/fill/video'Content Fill Modes#
Content fill modes control how video content is positioned within the block bounds.
Cover Mode#
Cover mode scales the video to completely fill the block area. Parts of the video may be cropped if the aspect ratios differ.
// Cover mode: Fill entire block, may crop videoconst coverBlock = engine.block.create('graphic');engine.block.setShape(coverBlock, engine.block.createShape('rect'));engine.block.setWidth(coverBlock, 200);engine.block.setHeight(coverBlock, 150);
const coverVideoFill = engine.block.createFill('video');engine.block.setString(coverVideoFill, 'fill/video/fileURI', videoUri);engine.block.setFill(coverBlock, coverVideoFill);engine.block.appendChild(page, coverBlock);
// Set content fill mode to Coverengine.block.setEnum(coverBlock, 'contentFill/mode', 'Cover');
// Get current fill modeconst coverMode = engine.block.getEnum(coverBlock, 'contentFill/mode');console.log('Cover block fill mode:', coverMode); // 'Cover'Contain Mode#
Contain mode scales the video to fit entirely within the block bounds. The complete video is visible, but empty space may appear if aspect ratios differ.
// Contain mode: Fit entire video, may leave empty spaceconst containBlock = engine.block.create('graphic');engine.block.setShape(containBlock, engine.block.createShape('rect'));engine.block.setWidth(containBlock, 200);engine.block.setHeight(containBlock, 150);
const containVideoFill = engine.block.createFill('video');engine.block.setString(containVideoFill, 'fill/video/fileURI', videoUri);engine.block.setFill(containBlock, containVideoFill);engine.block.appendChild(page, containBlock);
// Set content fill mode to Containengine.block.setEnum(containBlock, 'contentFill/mode', 'Contain');Getting the Current Mode#
// Get current fill modeconst currentMode = engine.block.getEnum(containBlock, 'contentFill/mode');console.log('Current fill mode:', currentMode);The available modes are:
'Crop'— Default mode, uses crop scale to position content'Cover'— Fill entire area, may crop content'Contain'— Show all content, may leave empty space
Loading Video Resources#
Video metadata like duration is only available after the resource loads. Use forceLoadAVResource() to ensure the video is loaded before accessing metadata.
// Force load video resource to access metadataconst metadataBlock = engine.block.create('graphic');engine.block.setShape(metadataBlock, engine.block.createShape('rect'));engine.block.setWidth(metadataBlock, 200);engine.block.setHeight(metadataBlock, 150);
const metadataVideoFill = engine.block.createFill('video');engine.block.setString(metadataVideoFill, 'fill/video/fileURI', videoUri);engine.block.setFill(metadataBlock, metadataVideoFill);engine.block.appendChild(page, metadataBlock);
// Force load the video resource before accessing metadataawait engine.block.forceLoadAVResource(metadataVideoFill);
// Now we can access video metadataconst totalDuration = engine.block.getDouble( metadataVideoFill, 'fill/video/totalDuration');console.log('Video total duration:', totalDuration, 'seconds');Common Use Cases#
Video in Shapes#
Fill any shape with video content:
// Fill a shape with video contentconst ellipseBlock = engine.block.create('graphic');const ellipseShape = engine.block.createShape('//ly.img.ubq/shape/ellipse');engine.block.setShape(ellipseBlock, ellipseShape);engine.block.setWidth(ellipseBlock, 200);engine.block.setHeight(ellipseBlock, 150);
const ellipseVideoFill = engine.block.createFill('video');engine.block.setString(ellipseVideoFill, 'fill/video/fileURI', videoUri);engine.block.setFill(ellipseBlock, ellipseVideoFill);engine.block.appendChild(page, ellipseBlock);Opacity Control#
Adjust block opacity for transparency effects:
// Control opacity for transparency effectsconst opacityBlock = engine.block.create('graphic');engine.block.setShape(opacityBlock, engine.block.createShape('rect'));engine.block.setWidth(opacityBlock, 200);engine.block.setHeight(opacityBlock, 150);
const opacityVideoFill = engine.block.createFill('video');engine.block.setString(opacityVideoFill, 'fill/video/fileURI', videoUri);engine.block.setFill(opacityBlock, opacityVideoFill);engine.block.appendChild(page, opacityBlock);
// Set block opacity to 70%engine.block.setFloat(opacityBlock, 'opacity', 0.7);Additional Techniques#
Shared Video Fills#
Multiple blocks can share the same fill instance, which improves memory efficiency when displaying the same video in multiple locations.
// Share one video fill between multiple blocks for memory efficiencyconst sharedFill = engine.block.createFill('video');engine.block.setString(sharedFill, 'fill/video/fileURI', videoUri);
// First block using shared fillconst sharedBlock1 = engine.block.create('graphic');engine.block.setShape(sharedBlock1, engine.block.createShape('rect'));engine.block.setWidth(sharedBlock1, 200);engine.block.setHeight(sharedBlock1, 150);engine.block.setFill(sharedBlock1, sharedFill);engine.block.appendChild(page, sharedBlock1);
// Second block using the same shared fillconst sharedBlock2 = engine.block.create('graphic');engine.block.setShape(sharedBlock2, engine.block.createShape('rect'));engine.block.setWidth(sharedBlock2, 160);engine.block.setHeight(sharedBlock2, 120);engine.block.setFill(sharedBlock2, sharedFill);engine.block.appendChild(page, sharedBlock2);
console.log('Shared fill - Two blocks using the same video fill instance');Cleanup#
Always dispose of the engine when finished to release resources.
} finally { engine.dispose();}Troubleshooting#
Video Fill Not Displaying#
If video fills don’t appear:
- Verify the scene is in Video mode (
engine.scene.getMode()returns'Video') - Ensure the video URI is accessible and valid
- Check that the block has a shape assigned before setting the fill
- Confirm the block has been appended to the page
Metadata Not Available#
If fill/video/totalDuration returns unexpected values:
- Call
engine.block.forceLoadAVResource()before accessing metadata - Await the promise to ensure loading completes
- Verify the video file exists and is accessible
API Reference#
| Property | Type | Description |
|---|---|---|
fill/video/fileURI | string | URI of the video file |
fill/video/totalDuration | double | Total duration of the video in seconds |
contentFill/mode | enum | Fill mode: ‘Crop’, ‘Cover’, ‘Contain’ |
opacity | float | Block opacity from 0.0 to 1.0 |