Add synchronized captions to video projects using CE.SDK’s caption system in headless mode, with support for importing subtitle files, styling programmatically, and saving scenes with captions.
Captions in CE.SDK follow a hierarchy: Page → CaptionTrack → Caption blocks. Each caption has text, timing (time offset and duration), and styling properties. Captions appear and disappear based on their timing, synchronized with video playback.
This guide covers how to import captions from SRT/VTT files, style them using custom properties, create captions programmatically, and save scenes with captions in a headless Node.js environment.
Understanding Caption Structure#
Caption Hierarchy#
CE.SDK organizes captions in a parent-child hierarchy. The page contains one or more caption tracks, and each caption track contains individual caption blocks. This structure allows for multiple caption tracks (for different languages or purposes) while keeping captions organized.
When you import captions from a subtitle file, CE.SDK automatically creates the caption track and populates it with caption blocks. Each caption block stores its text content, start time, duration, and styling properties.
Caption Timing#
Each caption has two timing properties: time offset (when the caption appears) and duration (how long it stays visible). These values are in seconds and synchronize with the video timeline. A caption with a time offset of 2.0 and duration of 3.0 appears at the 2-second mark and disappears at the 5-second mark.
Initialize CE.SDK#
For headless video processing with captions, we initialize CE.SDK’s Node.js engine. This provides full API access without browser dependencies, ideal for server-side automation and batch processing.
// Initialize CE.SDK engine in headless modeconst engine = await CreativeEngine.init({ // license: process.env.CESDK_LICENSE, // Optional (trial mode available)});The headless engine gives you complete control over caption operations, perfect for automated workflows, background processing, and server-side video preparation.
Creating a Video Scene#
Create a video scene with page configuration. The page option creates a page with the specified dimensions.
// Create a video scene with page configuration// The page option creates a page with specified dimensionsengine.scene.createVideo({ page: { size: { width: 1920, height: 1080 } }});
const page = engine.block.findByType('page')[0];
// Set page duration to accommodate video contentengine.block.setDuration(page, 10);Set the page duration to accommodate your video content. The dimensions define the output video resolution.
Importing Captions from Subtitle Files#
Using createCaptionsFromURI#
The fastest way to add captions is importing from an SRT or VTT subtitle file. CE.SDK parses the file and creates caption blocks with timing already configured.
// Import captions from SRT file// createCaptionsFromURI parses SRT/VTT and creates caption blocks with timingconst captionSrtUrl = 'https://img.ly/static/examples/captions.srt';const captionBlocks = await engine.block.createCaptionsFromURI(captionSrtUrl);
console.log(`Imported ${captionBlocks.length} captions from SRT file`);The createCaptionsFromURI method downloads the subtitle file, parses the timing and text, and creates a caption track with all captions positioned correctly. It returns an array of caption block IDs for the imported captions.
Creating the Caption Track#
After importing captions, create a caption track to organize them in the timeline.
// Create a caption track and add captions to it// Caption tracks organize captions in the timelineconst captionTrack = engine.block.create('//ly.img.ubq/captionTrack');engine.block.appendChild(page, captionTrack);
// Add each caption block to the trackfor (const captionId of captionBlocks) { engine.block.appendChild(captionTrack, captionId);}
console.log(`Caption track created with ${captionBlocks.length} captions`);Create a caption track with engine.block.create('//ly.img.ubq/captionTrack') and append it to the page. Then add each caption block to the track using appendChild.
Reading Caption Properties#
Retrieve caption properties for processing, validation, or logging.
// Read caption properties (text, timing)if (captionBlocks.length > 0) { const firstCaption = captionBlocks[0];
const text = engine.block.getString(firstCaption, 'caption/text'); const offset = engine.block.getTimeOffset(firstCaption); const duration = engine.block.getDuration(firstCaption);
console.log(`First caption: "${text}"`); console.log(` Time: ${offset}s - ${offset + duration}s`);}Use getString for text, getTimeOffset for start time, and getDuration for display length. These values are useful for validation or synchronization workflows.
Creating Captions Programmatically#
Caption Track Setup#
For full control over captions, create them programmatically. First, create a caption track and append it to the page.
const captionTrack = engine.block.create('//ly.img.ubq/captionTrack');engine.block.appendChild(page, captionTrack);Creating Caption Blocks#
Create individual captions with text and timing.
// Create a caption programmatically (in addition to imported ones)const manualCaption = engine.block.create('//ly.img.ubq/caption');engine.block.appendChild(captionTrack, manualCaption);
// Set caption textengine.block.setString(manualCaption, 'caption/text', 'Manual caption added');
// Set timing - appears at 8 seconds for 2 secondsengine.block.setTimeOffset(manualCaption, 8);engine.block.setDuration(manualCaption, 2);
// Apply same stylingengine.block.setFloat(manualCaption, 'caption/fontSize', 48);engine.block.setEnum(manualCaption, 'caption/horizontalAlignment', 'Center');engine.block.setEnum(manualCaption, 'caption/verticalAlignment', 'Bottom');engine.block.setBool(manualCaption, 'backgroundColor/enabled', true);engine.block.setColor(manualCaption, 'backgroundColor/color', { r: 0, g: 0, b: 0, a: 0.7});
console.log('Created manual caption at 8s');Set the caption text using setString with the caption/text property. Position the caption in time using setTimeOffset (when it appears) and setDuration (how long it shows).
Styling Captions#
Typography#
Control caption appearance with typography properties.
// Style all captions with consistent formattingfor (const captionId of captionBlocks) { // Set font size engine.block.setFloat(captionId, 'caption/fontSize', 48);
// Center alignment engine.block.setEnum(captionId, 'caption/horizontalAlignment', 'Center'); engine.block.setEnum(captionId, 'caption/verticalAlignment', 'Bottom');
// Enable background for readability engine.block.setBool(captionId, 'backgroundColor/enabled', true); engine.block.setColor(captionId, 'backgroundColor/color', { r: 0, g: 0, b: 0, a: 0.7 });}
console.log('Applied styling to all captions');Use setFloat for numeric values like caption/fontSize, caption/letterSpacing, and caption/lineHeight. Set alignment with setEnum for caption/horizontalAlignment (Left, Center, Right) and caption/verticalAlignment (Top, Center, Bottom).
Background#
Enable a background behind caption text for better readability over video content. Use setBool to enable backgroundColor/enabled and setColor to set backgroundColor/color with RGBA values. A semi-transparent black background (alpha 0.7) is common for video captions.
Automatic Font Sizing#
CE.SDK can automatically adjust font size to fit caption text within bounds. Enable automatic sizing and set minimum and maximum size limits.
engine.block.setBool(captionId, 'caption/automaticFontSizeEnabled', true);engine.block.setFloat(captionId, 'caption/minAutomaticFontSize', 24);engine.block.setFloat(captionId, 'caption/maxAutomaticFontSize', 72);This prevents text from overflowing while maintaining readability.
Caption Animations#
Adding Entry Animations#
Make captions more engaging by adding entry animations.
// Add entry animation to captionsfor (const captionId of captionBlocks) { const fadeIn = engine.block.createAnimation('fade'); engine.block.setDuration(fadeIn, 0.2); engine.block.setInAnimation(captionId, fadeIn);}
console.log('Added fade-in animations to captions');Create an animation using createAnimation with types like ‘fade’, ‘slide’, or ‘scale’. Set the animation duration and apply it with setInAnimation.
Animation Types#
CE.SDK supports several animation types for captions:
- fade - Opacity transition
- slide - Position movement
- scale - Size change
- blur - Focus effect
Set loop animations with setLoopAnimation for continuous effects, or exit animations with setOutAnimation for departure transitions.
Saving the Scene#
After adding captions, save the scene to a .scene file for later use or rendering with the CE.SDK Renderer service.
// Save scene to file for later rendering// The scene preserves all caption data, styling, and animationsconst sceneString = await engine.scene.saveToString();
// Save to output directoryconst outputDir = join(process.cwd(), 'output');await mkdir(outputDir, { recursive: true });
const outputPath = join(outputDir, 'video-with-captions.scene');await writeFile(outputPath, sceneString);
console.log(`Scene saved: ${outputPath}`);The scene file preserves all caption data including text, timing, styling, and animations. You can load this scene later for further editing or send it to the CE.SDK Renderer for video export.
Resource Cleanup#
Always dispose of the engine when processing is complete to free resources.
// Always dispose of the engine to free resourcesengine.dispose();Use a try/finally pattern to ensure cleanup happens even if an error occurs during processing.
Troubleshooting#
| Issue | Cause | Solution |
|---|---|---|
| Captions not visible | Not in caption track hierarchy | Check getParent(): page → captionTrack → caption |
| Wrong timing | Time offset/duration incorrect | Verify getTimeOffset() and getDuration() |
| Import fails | Unsupported format | Use valid SRT or VTT file |
| Styling not applying | Property path wrong | Use caption/ prefix for caption properties |
Captions Not Appearing#
If captions don’t appear in the exported video, verify the caption hierarchy. Each caption must be a child of a caption track, which must be a child of the page. Use getParent() to trace the hierarchy.
Also check that caption timing falls within the page duration. Captions outside the page’s time range won’t appear in the export.
Import Errors#
If createCaptionsFromURI fails, verify the URL is accessible from your server and returns valid SRT or VTT content. Common issues include network restrictions and malformed subtitle files. Test the URL with curl to confirm accessibility.
API Reference#
| Method | Purpose |
|---|---|
engine.scene.createVideo(options) | Create video scene with page |
engine.block.createCaptionsFromURI(uri) | Import captions from SRT/VTT file |
engine.block.create('//ly.img.ubq/captionTrack') | Create caption track container |
engine.block.create('//ly.img.ubq/caption') | Create caption block |
engine.block.setString(id, property, value) | Set caption text |
engine.block.setTimeOffset(id, offset) | Set caption start time |
engine.block.setDuration(id, duration) | Set caption display duration |
engine.block.setFloat(id, property, value) | Set font size, spacing |
engine.block.setEnum(id, property, value) | Set alignment |
engine.block.setBool(id, property, value) | Enable background |
engine.block.setColor(id, property, value) | Set colors |
engine.block.createAnimation(type) | Create animation |
engine.block.setInAnimation(id, animation) | Set entry animation |
engine.scene.saveToString() | Save scene to string |
engine.dispose() | Clean up engine resources |