Skip to main content

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.
Media Layer: This class processes videos locally using FFmpeg. No internet connection or credits required. For background removal (API layer), see VideoBGRemoverClient.

Creating Compositions

  • Node.js
  • Python
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)

Adding Layers

Add transparent videos as layers in your composition:
  • Node.js
  • Python
// 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)

Canvas Configuration

  • Node.js
  • Python
// 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

Export Methods

  • Node.js
  • Python
// 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)

LayerHandle Class

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

Positioning Methods

  • Node.js
  • Python
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

Sizing Methods

  • Node.js
  • Python
// 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

Visual Effects

  • Node.js
  • Python
// 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)

Timing Methods

  • Node.js
  • Python
// 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

Audio Control

  • Node.js
  • Python
// 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

Method Chaining

All LayerHandle methods return the handle itself, allowing for fluent chaining:
  • Node.js
  • Python
// 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

Duration Control

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

Rule 1: Video Background Controls Duration

  • Node.js
  • Python
// 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

Rule 2: Color/Image Backgrounds Use Longest Foreground

  • Node.js
  • Python
// 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)

Rule 3: Explicit Duration Override

  • Node.js
  • Python
// Force specific duration
const comp = new Composition(anyBackground)
comp.setDuration(45.0) // Force 45-second duration
comp.add(video)

// Final video: exactly 45 seconds

Audio System

The composition system automatically handles audio from multiple sources:

Single Audio Source

  • Node.js
  • Python
// Only one layer has audio - used directly
comp.add(videoWithAudio, 'main').audio(true, 1.0)
comp.add(videoNoAudio, 'overlay').audio(false)

Multiple Audio Sources

  • Node.js
  • Python
// 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

Advanced Examples

Multi-Layer News Layout

  • Node.js
  • Python
// 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())

Timed Presentation

  • Node.js
  • Python
// 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)

Debugging

Dry Run

See the FFmpeg command without executing:
  • Node.js
  • Python
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

Verbose Export

See FFmpeg output in real-time:
  • Node.js
  • Python
// Show FFmpeg output for debugging
await comp.toFile('debug.mp4', EncoderProfile.h264(), undefined, true)