A server-side video composition engine for Node.js — compose videos programmatically using JSON configurations with full support for images, video overlays, text, and audio tracks.
Features • How It Works • Quick Start • Configuration • API Reference • License
- 🖼️ Image Layers — Place background images or overlays with precise positioning, scaling, and rotation.
- 🎥 Video Overlays — Embed video clips with trimming, timeline control, and alpha compositing.
- ✏️ Dynamic Text — Add text layers via Fabric.js with full styling support (fonts, colors, positioning).
- 🔊 Audio Sync — Mix multiple audio tracks with trim and timeline alignment for professional output.
- ⏱️ Timeline System — Each clip has independent
display(when it appears) andtrim(source segment) controls in milliseconds. - 🖥️ Fully Headless — Runs entirely on the server with no browser, GPU, or display required.
- 📦 Single MP4 Output — Produces ready-to-use H.264 + AAC MP4 files via WebCodecs.
JSON Config → Fabric.js Canvas (per frame) → WebCodecs Encoder → MP4 Output
- Parse — A
VideoConfigJSON defines canvas size, FPS, duration, and an array of clips. - Compose — For each frame, clips are layered onto a Fabric.js
StaticCanvasbased on z-index and timeline visibility. - Encode — Each composed frame is fed to a WebCodecs encoder (via Mediabunny) and muxed into an MP4 container.
- Audio — Audio tracks are decoded, trimmed, time-shifted, and muxed in sync with video.
| Technology | Role |
|---|---|
| Mediabunny | WebCodecs-based video/audio encoding & muxing |
| Fabric.js | Server-side canvas composition (fabric/node) |
| TypeScript | Type-safe configuration and rendering logic |
| Node.js | Runtime environment (headless) |
- Node.js ≥ 18
- pnpm (recommended) or npm
git clone https://github.com/nickaroot/headless-video-composer.git
cd headless-video-composer
pnpm installPlace your media files in src/files/:
src/files/
├── background.png # Background image
├── clip.mp4 # Video overlay
└── music.mp3 # Audio track
pnpm devThe output file (output.mp4) will be generated in the project root.
The video composition is defined by a VideoConfig object with two main sections:
settings: {
width: 720, // Canvas width in pixels
height: 1280, // Canvas height in pixels
fps: 30, // Frames per second
bgColor: '#000000', // Background fill color
duration: 5000 // Total duration in milliseconds
}Each clip in the clips array represents a layer in the composition:
{
id: 'bg-image', // Unique identifier
type: 'Image', // 'Image' | 'Video' | 'Text' | 'Audio'
src: 'background.png', // Source file (resolved from src/files/)
left: 400, // X position (pixels)
top: 400, // Y position (pixels)
width: 720, // Display width
height: 1280, // Display height
angle: 0, // Rotation in degrees
zIndex: 1, // Layer order (higher = on top)
opacity: 1, // 0.0 to 1.0
flip: null, // Flip transform
display: { from: 0, to: 5000 }, // Timeline visibility (ms)
trim: { from: 2000, to: 5000 } // Source trim range (ms) — Video/Audio only
}| Type | Description | Required Fields |
|---|---|---|
Image |
Static image layer | src |
Video |
Video overlay with optional trim | src, optionally trim |
Text |
Text rendered via Fabric.js | text |
Audio |
Audio track (no visual) | src, optionally trim |
The main entry point. Accepts a VideoConfig object and renders the composition to an MP4 file.
import { renderVideo } from './core/renderer';
await renderVideo({
settings: { width: 1920, height: 1080, fps: 30, bgColor: '#000', duration: 10000 },
outputFile: 'my-video.mp4',
clips: [ /* ... */ ]
});src/
├── index.ts # Entry point & sample configuration
├── core/
│ └── renderer.ts # Main rendering loop (Fabric.js + WebCodecs)
├── types/
│ └── video.ts # TypeScript interfaces (Clip, VideoConfig, Settings)
├── utils/
│ └── polyfills.ts # WebCodecs polyfills for Node.js
└── files/ # Media assets directory (git-ignored)
├── background.png
├── clip.mp4
└── music.mp3
This project is licensed under the MIT License — see the LICENSE file for details.