> ## Documentation Index
> Fetch the complete documentation index at: https://docs.videobgremover.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Video Composition System - Complete SDK Reference

> Master multi-layer video composition with VideoBGRemover SDK. Create, position, and export professional videos with anchors, sizing modes, and FFmpeg.

## Composition Class

The `Composition` class is the core of the **media layer** - it handles local FFmpeg operations for professional video editing. No API calls or credits required.

<Info>
  **Media Layer**: This class processes videos locally using FFmpeg. No internet connection or credits required. For background removal (API layer), see [VideoBGRemoverClient](/api-reference/sdk-reference/client).
</Info>

### Creating Compositions

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    import { Composition, Background } from '@videobgremover/sdk'

    // With background
    const bg = Background.fromColor('#FF0000', 1920, 1080, 30)
    const comp = new Composition(bg)

    // With explicit canvas size
    const comp = Composition.canvas(1920, 1080, 30)

    // Empty composition (add background later)
    const comp = new Composition()
    comp.background(bg)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    from videobgremover import Composition, Background

    # With background
    bg = Background.from_color('#FF0000', 1920, 1080, 30)
    comp = Composition(bg)

    # With explicit canvas size
    comp = Composition.canvas(1920, 1080, 30)

    # Empty composition (add background later)
    comp = Composition()
    comp.background(bg)
    ```
  </Tab>
</Tabs>

### Adding Layers

Add transparent videos as layers in your composition:

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Add layer and get handle for configuration
    const layer = comp.add(transparent, 'main_video')

    // Configure the layer using fluent API
    layer.at(Anchor.CENTER)
         .size(SizeMode.CONTAIN)
         .opacity(0.9)

    // Or chain everything
    comp.add(transparent, 'pip')
      .at(Anchor.TOP_RIGHT, -50, 50)
      .size(SizeMode.CANVAS_PERCENT, { percent: 25 })
      .start(5.0)
      .duration(10.0)
      .opacity(0.8)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Add layer and get handle for configuration
    layer = comp.add(transparent, 'main_video')

    # Configure the layer using fluent API
    layer.at(Anchor.CENTER) \
         .size(SizeMode.CONTAIN) \
         .opacity(0.9)

    # Or chain everything
    comp.add(transparent, 'pip') \
        .at(Anchor.TOP_RIGHT, dx=-50, dy=50) \
        .size(SizeMode.CANVAS_PERCENT, percent=25) \
        .start(5.0) \
        .duration(10.0) \
        .opacity(0.8)
    ```
  </Tab>
</Tabs>

### Canvas Configuration

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Set explicit canvas dimensions
    const comp = new Composition(background)
    const resizedComp = comp.setCanvas(3840, 2160, 60) // 4K 60fps

    // Set explicit duration
    const timedComp = comp.setDuration(30.0) // Force 30-second duration
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Set explicit canvas dimensions
    comp = Composition(background)
    comp.set_canvas(3840, 2160, 60)  # 4K 60fps

    # Set explicit duration
    comp.set_duration(30.0)  # Force 30-second duration
    ```
  </Tab>
</Tabs>

### Export Methods

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Export to file
    await comp.toFile('output.mp4', EncoderProfile.h264())

    // With progress tracking
    const progressCallback = (status: string) => {
      console.log(`Export: ${status}`)
    }
    await comp.toFile('output.mp4', EncoderProfile.h264(), progressCallback)

    // Verbose output for debugging
    await comp.toFile('output.mp4', EncoderProfile.h264(), undefined, true)

    // See FFmpeg command without executing
    const command = comp.dryRun()
    console.log('FFmpeg command:', command)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Export to file
    comp.to_file('output.mp4', EncoderProfile.h264())

    # With progress tracking
    def progress_callback(status):
        print(f'Export: {status}')

    comp.to_file('output.mp4', EncoderProfile.h264(), on_progress=progress_callback)

    # Verbose output for debugging
    comp.to_file('output.mp4', EncoderProfile.h264(), verbose=True)

    # See FFmpeg command without executing
    command = comp.dry_run()
    print(f'FFmpeg command: {command}')
    ```
  </Tab>
</Tabs>

## LayerHandle Class

The `LayerHandle` provides a fluent API for configuring individual layers. You get a handle when calling `comp.add()`.

### Positioning Methods

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    const layer = comp.add(transparent, 'my_layer')

    // Basic anchor positioning
    layer.at(Anchor.CENTER)
    layer.at(Anchor.TOP_LEFT)
    layer.at(Anchor.BOTTOM_RIGHT)

    // With pixel offsets
    layer.at(Anchor.CENTER, 100, 50)     // 100px right, 50px down
    layer.at(Anchor.TOP_RIGHT, -30, 30)  // 30px from right edge, 30px from top

    // Custom expressions for dynamic positioning
    layer.xy('W/2 + 100*cos(2*PI*t/5)', 'H/2 + 100*sin(2*PI*t/5)') // Circular motion
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    layer = comp.add(transparent, 'my_layer')

    # Basic anchor positioning
    layer.at(Anchor.CENTER)
    layer.at(Anchor.TOP_LEFT)
    layer.at(Anchor.BOTTOM_RIGHT)

    # With pixel offsets
    layer.at(Anchor.CENTER, dx=100, dy=50)     # 100px right, 50px down
    layer.at(Anchor.TOP_RIGHT, dx=-30, dy=30)  # 30px from right edge, 30px from top

    # Custom expressions for dynamic positioning
    layer.xy('W/2 + 100*cos(2*PI*t/5)', 'H/2 + 100*sin(2*PI*t/5)')  # Circular motion
    ```
  </Tab>
</Tabs>

### Sizing Methods

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Fit within canvas (letterbox if needed)
    layer.size(SizeMode.CONTAIN)

    // Fill canvas (crop if needed)
    layer.size(SizeMode.COVER)

    // Exact pixel dimensions
    layer.size(SizeMode.PX, { width: 800, height: 600 })

    // Percentage of canvas
    layer.size(SizeMode.CANVAS_PERCENT, { percent: 50 })           // 50% square
    layer.size(SizeMode.CANVAS_PERCENT, { width: 75, height: 25 }) // 75% wide, 25% tall
    layer.size(SizeMode.CANVAS_PERCENT, { width: 60 })            // 60% wide, maintain aspect

    // Scale relative to original video
    layer.size(SizeMode.SCALE, { scale: 1.5 })           // 150% of original
    layer.size(SizeMode.SCALE, { width: 2.0, height: 0.8 }) // 200% wide, 80% tall

    // Fit to canvas dimensions
    layer.size(SizeMode.FIT_WIDTH)   // Scale to match canvas width
    layer.size(SizeMode.FIT_HEIGHT)  // Scale to match canvas height
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Fit within canvas (letterbox if needed)
    layer.size(SizeMode.CONTAIN)

    # Fill canvas (crop if needed)
    layer.size(SizeMode.COVER)

    # Exact pixel dimensions
    layer.size(SizeMode.PX, width=800, height=600)

    # Percentage of canvas
    layer.size(SizeMode.CANVAS_PERCENT, percent=50)           # 50% square
    layer.size(SizeMode.CANVAS_PERCENT, width=75, height=25)  # 75% wide, 25% tall
    layer.size(SizeMode.CANVAS_PERCENT, width=60)             # 60% wide, maintain aspect

    # Scale relative to original video
    layer.size(SizeMode.SCALE, scale=1.5)           # 150% of original
    layer.size(SizeMode.SCALE, width=2.0, height=0.8)  # 200% wide, 80% tall

    # Fit to canvas dimensions
    layer.size(SizeMode.FIT_WIDTH)   # Scale to match canvas width
    layer.size(SizeMode.FIT_HEIGHT)  # Scale to match canvas height
    ```
  </Tab>
</Tabs>

### Visual Effects

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Opacity (0.0 = invisible, 1.0 = opaque)
    layer.opacity(0.8)

    // Rotation in degrees
    layer.rotate(15.0)    // 15° clockwise
    layer.rotate(-45.0)   // 45° counter-clockwise

    // Cropping (x, y, width, height)
    layer.crop(10, 20, 800, 600)

    // Z-order (higher = in front)
    layer.z(10)

    // Alpha channel control
    layer.alpha(true)   // Use transparency (default)
    layer.alpha(false)  // Ignore transparency (opaque)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Opacity (0.0 = invisible, 1.0 = opaque)
    layer.opacity(0.8)

    # Rotation in degrees
    layer.rotate(15.0)    # 15° clockwise
    layer.rotate(-45.0)   # 45° counter-clockwise

    # Cropping (x, y, width, height)
    layer.crop(10, 20, 800, 600)

    # Z-order (higher = in front)
    layer.z(10)

    # Alpha channel control
    layer.alpha(enabled=True)   # Use transparency (default)
    layer.alpha(enabled=False)  # Ignore transparency (opaque)
    ```
  </Tab>
</Tabs>

### Timing Methods

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Composition timeline control
    layer.start(2.0)       // Start at 2 seconds
    layer.end(10.0)        // End at 10 seconds
    layer.duration(5.0)    // Show for 5 seconds (from start time)

    // Source trimming (which part of source video to use)
    layer.subclip(5, 15)   // Use seconds 5-15 of source video
    layer.subclip(3)       // Use from 3 seconds to end of source
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Composition timeline control
    layer.start(2.0)       # Start at 2 seconds
    layer.end(10.0)        # End at 10 seconds
    layer.duration(5.0)    # Show for 5 seconds (from start time)

    # Source trimming (which part of source video to use)
    layer.subclip(5, 15)   # Use seconds 5-15 of source video
    layer.subclip(3)       # Use from 3 seconds to end of source
    ```
  </Tab>
</Tabs>

### Audio Control

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Audio from this layer
    layer.audio(true, 1.0)   // Enable audio at full volume
    layer.audio(true, 0.5)   // Enable audio at 50% volume
    layer.audio(false)       // Disable audio
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Audio from this layer
    layer.audio(enabled=True, volume=1.0)   # Enable audio at full volume
    layer.audio(enabled=True, volume=0.5)   # Enable audio at 50% volume
    layer.audio(enabled=False)              # Disable audio
    ```
  </Tab>
</Tabs>

## Method Chaining

All LayerHandle methods return the handle itself, allowing for fluent chaining:

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Chain multiple operations
    comp.add(transparent, 'complex_layer')
      .at(Anchor.TOP_RIGHT, -50, 50)      // Position
      .size(SizeMode.CANVAS_PERCENT, { percent: 30 })  // Size
      .opacity(0.8)                       // Visual effect
      .rotate(10.0)                       // Visual effect
      .start(5.0)                         // Timing
      .duration(10.0)                     // Timing
      .audio(true, 0.7)                   // Audio
      .z(20)                              // Z-order
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Chain multiple operations
    comp.add(transparent, 'complex_layer') \
        .at(Anchor.TOP_RIGHT, dx=-50, dy=50) \      # Position
        .size(SizeMode.CANVAS_PERCENT, percent=30) \  # Size
        .opacity(0.8) \                             # Visual effect
        .rotate(10.0) \                             # Visual effect
        .start(5.0) \                               # Timing
        .duration(10.0) \                           # Timing
        .audio(enabled=True, volume=0.7) \          # Audio
        .z(20)                                      # Z-order
    ```
  </Tab>
</Tabs>

## Duration Control

Compositions follow a simple 3-rule system for determining duration:

### Rule 1: Video Background Controls Duration

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // 30-second background video = 30-second composition
    const bg = Background.fromVideo('30sec_background.mp4')
    const comp = new Composition(bg)
    comp.add(shortForeground) // Even if foreground is 5 seconds

    // Final video: 30 seconds
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # 30-second background video = 30-second composition
    bg = Background.from_video('30sec_background.mp4')
    comp = Composition(bg)
    comp.add(short_foreground)  # Even if foreground is 5 seconds

    # Final video: 30 seconds
    ```
  </Tab>
</Tabs>

### Rule 2: Color/Image Backgrounds Use Longest Foreground

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Color background adapts to content
    const bg = Background.fromColor('#FF0000', 1920, 1080, 30)
    const comp = new Composition(bg)
    comp.add(tenSecondVideo)    // 10-second video
    comp.add(fiveSecondVideo)   // 5-second video

    // Final video: 10 seconds (longest foreground)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Color background adapts to content
    bg = Background.from_color('#FF0000', 1920, 1080, 30)
    comp = Composition(bg)
    comp.add(ten_second_video)   # 10-second video
    comp.add(five_second_video)  # 5-second video

    # Final video: 10 seconds (longest foreground)
    ```
  </Tab>
</Tabs>

### Rule 3: Explicit Duration Override

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Force specific duration
    const comp = new Composition(anyBackground)
    comp.setDuration(45.0) // Force 45-second duration
    comp.add(video)

    // Final video: exactly 45 seconds
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Force specific duration
    comp = Composition(any_background)
    comp.set_duration(45.0)  # Force 45-second duration
    comp.add(video)

    # Final video: exactly 45 seconds
    ```
  </Tab>
</Tabs>

## Audio System

The composition system automatically handles audio from multiple sources:

### Single Audio Source

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Only one layer has audio - used directly
    comp.add(videoWithAudio, 'main').audio(true, 1.0)
    comp.add(videoNoAudio, 'overlay').audio(false)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Only one layer has audio - used directly
    comp.add(video_with_audio, 'main').audio(enabled=True, volume=1.0)
    comp.add(video_no_audio, 'overlay').audio(enabled=False)
    ```
  </Tab>
</Tabs>

### Multiple Audio Sources

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Multiple layers with audio - automatically mixed
    comp.add(narrator, 'voice').audio(true, 0.8)      // Main audio at 80%
    comp.add(background, 'music').audio(true, 0.3)    // Background music at 30%
    comp.add(effects, 'sfx').audio(true, 0.6)         // Sound effects at 60%

    // System automatically creates audio mix
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Multiple layers with audio - automatically mixed
    comp.add(narrator, 'voice').audio(enabled=True, volume=0.8)      # Main audio at 80%
    comp.add(background, 'music').audio(enabled=True, volume=0.3)    # Background music at 30%
    comp.add(effects, 'sfx').audio(enabled=True, volume=0.6)         # Sound effects at 60%

    # System automatically creates audio mix
    ```
  </Tab>
</Tabs>

## Advanced Examples

### Multi-Layer News Layout

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // News broadcast layout
    const comp = new Composition(Background.fromColor('#1E293B', 1920, 1080, 30))

    // Main presenter (center-right)
    comp.add(presenter, 'presenter')
      .at(Anchor.CENTER_RIGHT, -100)
      .size(SizeMode.CANVAS_PERCENT, { percent: 55 })

    // Breaking news ticker (bottom)
    comp.add(tickerVideo, 'ticker')
      .at(Anchor.BOTTOM_CENTER, 0, -20)
      .size(SizeMode.CANVAS_PERCENT, { width: 90, height: 8 })

    // Logo (top-left)
    comp.add(logoVideo, 'logo')
      .at(Anchor.TOP_LEFT, 30, 30)
      .size(SizeMode.CANVAS_PERCENT, { percent: 12 })
      .opacity(0.9)

    await comp.toFile('news_broadcast.mp4', EncoderProfile.h264())
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # News broadcast layout
    comp = Composition(Background.from_color('#1E293B', 1920, 1080, 30))

    # Main presenter (center-right)
    comp.add(presenter, 'presenter') \
        .at(Anchor.CENTER_RIGHT, dx=-100) \
        .size(SizeMode.CANVAS_PERCENT, percent=55)

    # Breaking news ticker (bottom)
    comp.add(ticker_video, 'ticker') \
        .at(Anchor.BOTTOM_CENTER, dy=-20) \
        .size(SizeMode.CANVAS_PERCENT, width=90, height=8)

    # Logo (top-left)
    comp.add(logo_video, 'logo') \
        .at(Anchor.TOP_LEFT, dx=30, dy=30) \
        .size(SizeMode.CANVAS_PERCENT, percent=12) \
        .opacity(0.9)

    comp.to_file('news_broadcast.mp4', EncoderProfile.h264())
    ```
  </Tab>
</Tabs>

### Timed Presentation

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Presentation with timed elements
    const comp = new Composition(Background.fromImage('slide_bg.jpg', 30))

    // Speaker appears throughout
    comp.add(speaker, 'speaker')
      .at(Anchor.CENTER_LEFT, 100)
      .size(SizeMode.CANVAS_PERCENT, { percent: 40 })

    // Slide 1: 0-10 seconds
    comp.add(slide1, 'slide1')
      .start(0).duration(10)
      .at(Anchor.CENTER_RIGHT, -100)
      .size(SizeMode.CANVAS_PERCENT, { percent: 45 })

    // Slide 2: 10-20 seconds
    comp.add(slide2, 'slide2')
      .start(10).duration(10)
      .at(Anchor.CENTER_RIGHT, -100)
      .size(SizeMode.CANVAS_PERCENT, { percent: 45 })

    // Conclusion: 20-25 seconds
    comp.add(conclusion, 'conclusion')
      .start(20).duration(5)
      .at(Anchor.CENTER)
      .size(SizeMode.CONTAIN)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Presentation with timed elements
    comp = Composition(Background.from_image('slide_bg.jpg', fps=30))

    # Speaker appears throughout
    comp.add(speaker, 'speaker') \
        .at(Anchor.CENTER_LEFT, dx=100) \
        .size(SizeMode.CANVAS_PERCENT, percent=40)

    # Slide 1: 0-10 seconds
    comp.add(slide1, 'slide1') \
        .start(0).duration(10) \
        .at(Anchor.CENTER_RIGHT, dx=-100) \
        .size(SizeMode.CANVAS_PERCENT, percent=45)

    # Slide 2: 10-20 seconds
    comp.add(slide2, 'slide2') \
        .start(10).duration(10) \
        .at(Anchor.CENTER_RIGHT, dx=-100) \
        .size(SizeMode.CANVAS_PERCENT, percent=45)

    # Conclusion: 20-25 seconds
    comp.add(conclusion, 'conclusion') \
        .start(20).duration(5) \
        .at(Anchor.CENTER) \
        .size(SizeMode.CONTAIN)
    ```
  </Tab>
</Tabs>

## Debugging

### Dry Run

See the FFmpeg command without executing:

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    const command = comp.dryRun()
    console.log('FFmpeg command:')
    console.log(command)

    // Example output:
    // ffmpeg -y -f lavfi -i color=c=#FF0000:size=1920x1080:rate=30 
    // -i transparent.webm -filter_complex "[1:v]scale=1920:1080:force_original_aspect_ratio=decrease[layer_0_scale];[0:v][layer_0_scale]overlay=x='(W-w)/2':y='(H-h)/2'[out]" 
    // -map [out] -map 1:a? -c:v libx264 -crf 18 -preset medium output.mp4
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    command = comp.dry_run()
    print('FFmpeg command:')
    print(command)

    # Example output:
    # ffmpeg -y -f lavfi -i color=c=#FF0000:size=1920x1080:rate=30 
    # -i transparent.webm -filter_complex "[1:v]scale=1920:1080:force_original_aspect_ratio=decrease[layer_0_scale];[0:v][layer_0_scale]overlay=x='(W-w)/2':y='(H-h)/2'[out]" 
    # -map [out] -map 1:a? -c:v libx264 -crf 18 -preset medium output.mp4
    ```
  </Tab>
</Tabs>

### Verbose Export

See FFmpeg output in real-time:

<Tabs group="code-examples">
  <Tab title="Node.js">
    ```typescript theme={"dark"}
    // Show FFmpeg output for debugging
    await comp.toFile('debug.mp4', EncoderProfile.h264(), undefined, true)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"dark"}
    # Show FFmpeg output for debugging
    comp.to_file('debug.mp4', EncoderProfile.h264(), verbose=True)
    ```
  </Tab>
</Tabs>

## Related Classes

* **[Background](/video-composition/backgrounds)**: Background creation guide
* **[EncoderProfile](/api-reference/sdk-reference/encoders)**: Export format configuration
* **[Video](/api-reference/sdk-reference/video)**: Video loading and background removal
* **[Positioning Guide](/video-composition/positioning)**: Complete positioning and sizing guide
* **[Timing Guide](/video-composition/timing)**: Timeline and duration control
