Component System Guide
Designers: For the curated component catalog and examples, see Components.
Overview
Babulus uses a blank slate architecture - scenes start completely empty (black background, no elements) and you explicitly add visual components to build your video. This gives you full control over what appears on screen.
Key Concepts:
- Components - Visual elements you add to scenes (titles, subtitles, progress bars, backgrounds)
- Blank Slate - Scenes have nothing by default, you build up from scratch
- Data Binding - Components can display data from scenes and cues dynamically
- Layering - Control what appears in front/behind using zIndex
- Timing - Show/hide components at specific times
Default Behavior
Scenes start blank (black background, no elements). Add components explicitly to build your video. This gives you complete control over what appears on screen.
Quick Start
Basic Scene with Components
import { defineVideo } from "@babulus/dsl";
export default defineVideo((video) => {
video.composition("My Video", (composition) => {
composition.meta({ fps: 30, width: 1280, height: 720 });
composition.scene("intro", (scene) => {
scene.title("Welcome to Babulus");
// Add components
scene.components = [
{
id: "background",
type: "Background",
props: {
gradient: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
}
},
{
id: "title",
type: "Title",
bindings: {
text: "scene.title" // Bind to scene title
},
props: {
fontSize: 56,
color: "#ffffff",
position: { x: 48, y: 48 }
}
},
{
id: "progress",
type: "ProgressBar",
props: {
position: "bottom",
height: 8
}
}
];
scene.cue("intro-cue", (cue) => {
cue.voice((v) => v.say("Welcome to our platform!"));
});
});
});
});Built-in Components
Background Component
Sets the scene background - supports solid colors, gradients, and images.
{
id: "bg",
type: "Background",
props: {
color: "#1a1a1a" // Solid color
// OR
gradient: "linear-gradient(90deg, #667eea, #764ba2)" // CSS gradient
// OR
image: "url('./assets/background.jpg')" // Image URL
}
}Props:
color(string) - Solid background color (default: "#000000")gradient(string) - CSS gradient stringimage(string) - CSS background-image value
Note: Background always renders behind other components (zIndex: -1).
Title Component
Displays large heading text, typically bound to scene title.
{
id: "title",
type: "Title",
bindings: {
text: "scene.title" // Data binding
},
props: {
fontSize: 48,
fontWeight: 700,
color: "#ffffff",
textAlign: "center",
position: { x: 48, y: 48 }
}
}Props:
text(string) - Explicit text to displaybinding(string) - Data reference (e.g., "scene.title")fontSize(number) - Font size in pixels (default: 48)fontWeight(number | string) - CSS font-weight (default: 700)color(string) - Text color (default: "#ffffff")textAlign("left" | "center" | "right") - Alignment (default: "left")position(object) - { x, y } position in pixels (default: { x: 48, y: 48 })
Subtitle Component
Displays smaller text, typically bound to active cue text.
{
id: "subtitle",
type: "Subtitle",
bindings: {
text: "cue.text" // Default: shows active cue text
},
props: {
fontSize: 20,
color: "#cbd5f5",
position: { x: 48, y: 120 }
}
}Props: Same as Title component.
Default Behavior: If no binding is specified, defaults to "cue.text" to show the active cue's spoken text.
ProgressBar Component
Shows video playback progress.
{
id: "progress",
type: "ProgressBar",
props: {
position: "bottom", // "top" or "bottom"
height: 8,
color: "linear-gradient(90deg, #38bdf8, #818cf8)",
backgroundColor: "rgba(148,163,184,0.25)"
}
}Props:
position("top" | "bottom") - Bar position (default: "bottom")height(number) - Height in pixels (default: 8)color(string) - Fill color/gradient (default: blue gradient)backgroundColor(string) - Track color (default: semi-transparent gray)
Note: Progress is automatically calculated and injected by the renderer.
Data Binding
Components can reference dynamic data using the bindings property.
Available Data References
Scene Data:
scene.title- Scene titlescene.id- Scene ID
Cue Data:
cue.text- Active cue's text contentcue.label- Active cue's labelcue.id- Active cue's ID
Frame Data:
frame.number- Current frame numberframe.time- Current time in seconds
Binding Examples
// Title bound to scene title
{
id: "title",
type: "Title",
bindings: { text: "scene.title" }
}
// Subtitle showing current cue text
{
id: "subtitle",
type: "Subtitle",
bindings: { text: "cue.text" } // Updates as cues change
}
// Static text (no binding)
{
id: "watermark",
type: "Title",
props: {
text: "© 2026 My Company", // Fixed text
fontSize: 14,
position: { x: 20, y: 680 }
}
}Layering with zIndex
Control which components appear in front or behind using zIndex.
scene.components = [
{
id: "bg",
type: "Background",
zIndex: -1 // Always in back
},
{
id: "title",
type: "Title",
zIndex: 10 // In front
},
{
id: "progress",
type: "ProgressBar",
zIndex: 100 // Always on top
}
];Rules:
- Higher zIndex = appears in front
- Lower zIndex = appears behind
- Default zIndex is 0
- Background component defaults to -1
Component Timing
Show/hide components at specific times using the timing property.
{
id: "intro-title",
type: "Title",
props: { text: "Welcome!" },
timing: {
startSec: 0.5, // Appear at 0.5s
endSec: 3.0 // Disappear at 3.0s
}
}Timing Options:
startSec(number) - Time in seconds when component appearsendSec(number) - Time in seconds when component disappears- Omit
startSecto show from beginning - Omit
endSecto show until end
Visibility Control
Hide components conditionally using the visible property.
{
id: "debug-info",
type: "Title",
props: { text: "Debug Mode" },
visible: false // Hidden
}Complete Example
Here's a full scene with multiple components demonstrating all features:
import { defineVideo } from "@babulus/dsl";
export default defineVideo((video) => {
video.composition("Product Demo", (composition) => {
composition.meta({ fps: 30, width: 1920, height: 1080 });
composition.voiceover({ provider: "elevenlabs" });
composition.scene("introduction", (scene) => {
scene.title("Welcome to Our Product");
// Build up the scene with components
scene.components = [
// Background (always behind)
{
id: "background",
type: "Background",
props: {
gradient: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
}
},
// Scene title (appears after 0.5s)
{
id: "scene-title",
type: "Title",
bindings: {
text: "scene.title"
},
props: {
fontSize: 64,
fontWeight: 800,
color: "#ffffff",
textAlign: "center",
position: { x: 960, y: 300 }
},
timing: {
startSec: 0.5
},
zIndex: 10
},
// Subtitle showing active cue text
{
id: "subtitle",
type: "Subtitle",
bindings: {
text: "cue.text"
},
props: {
fontSize: 28,
color: "#e0e7ff",
textAlign: "center",
position: { x: 960, y: 900 }
},
zIndex: 20
},
// Progress bar (always on top)
{
id: "progress",
type: "ProgressBar",
props: {
position: "bottom",
height: 6
},
zIndex: 100
},
// Watermark (bottom-left corner)
{
id: "watermark",
type: "Title",
props: {
text: "© 2026 Acme Corp",
fontSize: 16,
fontWeight: 400,
color: "rgba(255,255,255,0.4)",
position: { x: 30, y: 1020 }
},
zIndex: 50
}
];
// Add voiceover cues
scene.cue("intro-1", (cue) => {
cue.voice((v) => {
v.say("Welcome to our revolutionary product.");
});
});
scene.cue("intro-2", (cue) => {
cue.voice((v) => {
v.say("Let me show you what makes it special.");
});
});
});
});
});Best Practices
Component Organization
- Order by zIndex: List components from back to front for clarity
- Unique IDs: Use descriptive, unique IDs for each component
- Consistent Naming: Use kebab-case for component IDs
Data Binding Usage
- Scene Title: Use
scene.titlebinding for scene titles - Cue Text: Use
cue.textfor subtitles that follow voice - Static Content: Use explicit
textprop for watermarks, logos
Performance Tips
- Minimize Components: Only add components you need
- Use Timing: Hide components when not needed using timing
- Reuse Types: Use same component type with different props
Troubleshooting
Component Not Appearing
- Check
visibleproperty is not set tofalse - Verify
timing.startSecandtiming.endSecare correct - Check component
typematches registered component name - Ensure
componentsarray is assigned to scene
Data Binding Not Working
- Verify binding syntax:
"scene.title","cue.text", etc. - Check that bound data exists (scene has title, cue has text)
- Ensure
bindingsobject is used, notprops
Wrong Layering Order
- Check
zIndexvalues (higher = front) - Background component should have negative zIndex
- Progress bar should have highest zIndex
Scene is Blank
- This is expected! Scenes are blank by default
- Add components to
scene.componentsarray - Start with a Background component to see something on screen
Migration from Markup
If you have older videos using .markup(), here's how to migrate to components:
Before (Markup-based)
scene.markup({
visualStyle: "modern",
primaryGradient: "linear-gradient(135deg, #667eea, #764ba2)"
});After (Component-based)
scene.components = [
{
id: "bg",
type: "Background",
props: {
gradient: "linear-gradient(135deg, #667eea, #764ba2)"
}
},
{
id: "title",
type: "Title",
bindings: { text: "scene.title" }
},
{
id: "subtitle",
type: "Subtitle",
bindings: { text: "cue.text" }
},
{
id: "progress",
type: "ProgressBar"
}
];Next Steps
- Explore Built-in Components: Try different props and styling
- Create Custom Layouts: Experiment with positioning and layering
- Use Data Binding: Make components dynamic with scene/cue data
- Build Templates: Create reusable component sets for consistent branding
Support
For questions or issues:
- Check the troubleshooting section above
- Review example videos in the test-projects folder
- Consult the DSL reference documentation