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 string
  • image (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 display
  • binding (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 title
  • scene.id - Scene ID

Cue Data:

  • cue.text - Active cue's text content
  • cue.label - Active cue's label
  • cue.id - Active cue's ID

Frame Data:

  • frame.number - Current frame number
  • frame.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 appears
  • endSec (number) - Time in seconds when component disappears
  • Omit startSec to show from beginning
  • Omit endSec to 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.title binding for scene titles
  • Cue Text: Use cue.text for subtitles that follow voice
  • Static Content: Use explicit text prop 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 visible property is not set to false
  • Verify timing.startSec and timing.endSec are correct
  • Check component type matches registered component name
  • Ensure components array 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 bindings object is used, not props

Wrong Layering Order

  • Check zIndex values (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.components array
  • 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