Adding Overlays
Complete examples for adding watermarks, logos, background music, and complex overlays
Learn how to enhance your videos with watermarks, logos, background music, picture-in-picture overlays, and animated GIFs. This guide provides production-ready examples for creating professional, branded video content.
Overview
Video overlays allow you to add:
- Image overlays - Watermarks, logos, branding
- Video overlays - Picture-in-picture, reactions
- Audio overlays - Background music, sound effects
- GIF overlays - Animated stickers, reactions
- Multiple layers - Complex compositions with z-index
Adding a Watermark
The most common overlay: add a watermark logo to your video.
async function addWatermark(videoUrl, watermarkUrl) {
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
elements: [
{
type: 'image',
url: watermarkUrl,
timing: { entireVideo: true },
position: { anchor: 'bottom-right' },
size: { width: '15%' },
effects: { opacity: 0.7 },
zIndex: 100
}
]
}),
});
const data = await response.json();
return data.videoId;
}
// Usage
const videoId = await addWatermark(
'https://example.com/video.mp4',
'https://example.com/watermark.png'
);
const result = await waitForCompletion(videoId);
console.log('Watermarked video:', result.videoUrl);def add_watermark(video_url, watermark_url):
"""Add watermark to video"""
response = requests.post(
'https://api.videocascade.com/v1/videos',
headers={
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
json={
'fileUrl': video_url,
'elements': [
{
'type': 'image',
'url': watermark_url,
'timing': {'entireVideo': True},
'position': {'anchor': 'bottom-right'},
'size': {'width': '15%'},
'effects': {'opacity': 0.7},
'zIndex': 100
}
]
}
)
response.raise_for_status()
data = response.json()
return data['videoId']
# Usage
video_id = add_watermark(
'https://example.com/video.mp4',
'https://example.com/watermark.png'
)
result = wait_for_completion(video_id)
print(f"Watermarked video: {result['videoUrl']}")interface ImageElement {
type: 'image';
url: string;
timing: { entireVideo: boolean };
position: { anchor: string };
size: { width: string };
effects?: { opacity: number };
zIndex?: number;
}
async function addWatermark(
videoUrl: string,
watermarkUrl: string
): Promise<string> {
const watermark: ImageElement = {
type: 'image',
url: watermarkUrl,
timing: { entireVideo: true },
position: { anchor: 'bottom-right' },
size: { width: '15%' },
effects: { opacity: 0.7 },
zIndex: 100
};
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
elements: [watermark]
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.videoId;
}
// Usage
const videoId = await addWatermark(
'https://example.com/video.mp4',
'https://example.com/watermark.png'
);
const result = await waitForCompletion(videoId);
console.log('Watermarked video:', result.videoUrl);Positioned Logo
Add a logo in different positions with precise control.
async function addPositionedLogo(videoUrl, logoUrl, position = 'top-right') {
const positions = {
'top-left': { anchor: 'top-left' },
'top-right': { anchor: 'top-right' },
'top-center': { anchor: 'top-center' },
'bottom-left': { anchor: 'bottom-left' },
'bottom-right': { anchor: 'bottom-right' },
'bottom-center': { anchor: 'bottom-center' },
'center': { anchor: 'center' }
};
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
elements: [
{
type: 'image',
url: logoUrl,
timing: { entireVideo: true },
position: positions[position],
size: { width: '12%' },
effects: {
opacity: 0.9,
fadeIn: { duration: 0.5 }
},
zIndex: 100
}
]
}),
});
const data = await response.json();
return data.videoId;
}
// Usage examples
await addPositionedLogo(videoUrl, logoUrl, 'top-right'); // Top right corner
await addPositionedLogo(videoUrl, logoUrl, 'bottom-left'); // Bottom left corner
await addPositionedLogo(videoUrl, logoUrl, 'center'); // Center of videodef add_positioned_logo(video_url, logo_url, position='top-right'):
"""Add logo at specified position"""
positions = {
'top-left': {'anchor': 'top-left'},
'top-right': {'anchor': 'top-right'},
'top-center': {'anchor': 'top-center'},
'bottom-left': {'anchor': 'bottom-left'},
'bottom-right': {'anchor': 'bottom-right'},
'bottom-center': {'anchor': 'bottom-center'},
'center': {'anchor': 'center'}
}
response = requests.post(
'https://api.videocascade.com/v1/videos',
headers={
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
json={
'fileUrl': video_url,
'elements': [
{
'type': 'image',
'url': logo_url,
'timing': {'entireVideo': True},
'position': positions[position],
'size': {'width': '12%'},
'effects': {
'opacity': 0.9,
'fadeIn': {'duration': 0.5}
},
'zIndex': 100
}
]
}
)
response.raise_for_status()
data = response.json()
return data['videoId']
# Usage examples
add_positioned_logo(video_url, logo_url, 'top-right')
add_positioned_logo(video_url, logo_url, 'bottom-left')
add_positioned_logo(video_url, logo_url, 'center')type Position = 'top-left' | 'top-right' | 'top-center' |
'bottom-left' | 'bottom-right' | 'bottom-center' |
'center';
async function addPositionedLogo(
videoUrl: string,
logoUrl: string,
position: Position = 'top-right'
): Promise<string> {
const positions: Record<Position, { anchor: string }> = {
'top-left': { anchor: 'top-left' },
'top-right': { anchor: 'top-right' },
'top-center': { anchor: 'top-center' },
'bottom-left': { anchor: 'bottom-left' },
'bottom-right': { anchor: 'bottom-right' },
'bottom-center': { anchor: 'bottom-center' },
'center': { anchor: 'center' }
};
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
elements: [
{
type: 'image',
url: logoUrl,
timing: { entireVideo: true },
position: positions[position],
size: { width: '12%' },
effects: {
opacity: 0.9,
fadeIn: { duration: 0.5 }
},
zIndex: 100
}
]
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.videoId;
}
// Usage examples
await addPositionedLogo(videoUrl, logoUrl, 'top-right');
await addPositionedLogo(videoUrl, logoUrl, 'bottom-left');
await addPositionedLogo(videoUrl, logoUrl, 'center');Background Music
Add background music with volume control and fade effects.
async function addBackgroundMusic(videoUrl, musicUrl) {
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
normalizeAudio: true, // Balance music with original audio
elements: [
{
type: 'audio',
url: musicUrl,
timing: { entireVideo: true },
effects: {
volume: 0.2, // 20% volume (subtle background)
fadeIn: { duration: 2, easing: 'ease-in' },
fadeOut: { duration: 3, easing: 'ease-out' }
},
loop: true // Loop if music is shorter than video
}
]
}),
});
const data = await response.json();
return data.videoId;
}
// Usage with different music volumes
async function addMusicWithVolume(videoUrl, musicUrl, volume = 0.2) {
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
normalizeAudio: true,
elements: [
{
type: 'audio',
url: musicUrl,
timing: { entireVideo: true },
effects: {
volume,
fadeIn: { duration: 2 },
fadeOut: { duration: 3 }
},
loop: true
}
]
}),
});
return (await response.json()).videoId;
}
// Usage
await addMusicWithVolume(videoUrl, musicUrl, 0.15); // Subtle (15%)
await addMusicWithVolume(videoUrl, musicUrl, 0.3); // Moderate (30%)
await addMusicWithVolume(videoUrl, musicUrl, 0.5); // Prominent (50%)def add_background_music(video_url, music_url):
"""Add background music to video"""
response = requests.post(
'https://api.videocascade.com/v1/videos',
headers={
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
json={
'fileUrl': video_url,
'normalizeAudio': True, # Balance music with original audio
'elements': [
{
'type': 'audio',
'url': music_url,
'timing': {'entireVideo': True},
'effects': {
'volume': 0.2, # 20% volume
'fadeIn': {'duration': 2, 'easing': 'ease-in'},
'fadeOut': {'duration': 3, 'easing': 'ease-out'}
},
'loop': True
}
]
}
)
response.raise_for_status()
data = response.json()
return data['videoId']
def add_music_with_volume(video_url, music_url, volume=0.2):
"""Add music with custom volume"""
response = requests.post(
'https://api.videocascade.com/v1/videos',
headers={
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
json={
'fileUrl': video_url,
'normalizeAudio': True,
'elements': [
{
'type': 'audio',
'url': music_url,
'timing': {'entireVideo': True},
'effects': {
'volume': volume,
'fadeIn': {'duration': 2},
'fadeOut': {'duration': 3}
},
'loop': True
}
]
}
)
response.raise_for_status()
return response.json()['videoId']
# Usage
add_music_with_volume(video_url, music_url, 0.15) # Subtle
add_music_with_volume(video_url, music_url, 0.3) # Moderate
add_music_with_volume(video_url, music_url, 0.5) # Prominentinterface AudioElement {
type: 'audio';
url: string;
timing: { entireVideo: boolean };
effects: {
volume: number;
fadeIn?: { duration: number; easing?: string };
fadeOut?: { duration: number; easing?: string };
};
loop: boolean;
}
async function addBackgroundMusic(
videoUrl: string,
musicUrl: string
): Promise<string> {
const music: AudioElement = {
type: 'audio',
url: musicUrl,
timing: { entireVideo: true },
effects: {
volume: 0.2,
fadeIn: { duration: 2, easing: 'ease-in' },
fadeOut: { duration: 3, easing: 'ease-out' }
},
loop: true
};
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
normalizeAudio: true,
elements: [music]
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.videoId;
}
async function addMusicWithVolume(
videoUrl: string,
musicUrl: string,
volume: number = 0.2
): Promise<string> {
const music: AudioElement = {
type: 'audio',
url: musicUrl,
timing: { entireVideo: true },
effects: {
volume,
fadeIn: { duration: 2 },
fadeOut: { duration: 3 }
},
loop: true
};
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
normalizeAudio: true,
elements: [music]
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return (await response.json()).videoId;
}
// Usage
await addMusicWithVolume(videoUrl, musicUrl, 0.15); // Subtle
await addMusicWithVolume(videoUrl, musicUrl, 0.3); // Moderate
await addMusicWithVolume(videoUrl, musicUrl, 0.5); // ProminentPicture-in-Picture Video Overlay
Add a webcam or reaction video in the corner.
async function addPictureInPicture(mainVideoUrl, pipVideoUrl) {
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: mainVideoUrl,
elements: [
{
type: 'video',
url: pipVideoUrl,
timing: { entireVideo: true },
position: { anchor: 'bottom-right' },
size: {
width: '25%', // 1/4 of main video width
fit: 'cover'
},
effects: {
fadeIn: { duration: 0.5 },
fadeOut: { duration: 0.5 }
},
zIndex: 10,
loop: false // Don't loop the PiP video
}
]
}),
});
const data = await response.json();
return data.videoId;
}
// Usage: Add webcam overlay to screen recording
const videoId = await addPictureInPicture(
'https://example.com/screen-recording.mp4',
'https://example.com/webcam.mp4'
);
// Advanced: PiP with timing control
async function addTimedPiP(mainVideoUrl, pipVideoUrl, startTime, endTime) {
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: mainVideoUrl,
elements: [
{
type: 'video',
url: pipVideoUrl,
timing: { startTime, endTime }, // Show only during this time
position: { anchor: 'bottom-right' },
size: { width: '25%', fit: 'cover' },
effects: {
fadeIn: { duration: 0.5 },
fadeOut: { duration: 0.5 }
},
zIndex: 10
}
]
}),
});
return (await response.json()).videoId;
}
// Show PiP only from 30s to 90s
await addTimedPiP(mainVideoUrl, pipVideoUrl, 30, 90);def add_picture_in_picture(main_video_url, pip_video_url):
"""Add picture-in-picture video overlay"""
response = requests.post(
'https://api.videocascade.com/v1/videos',
headers={
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
json={
'fileUrl': main_video_url,
'elements': [
{
'type': 'video',
'url': pip_video_url,
'timing': {'entireVideo': True},
'position': {'anchor': 'bottom-right'},
'size': {
'width': '25%',
'fit': 'cover'
},
'effects': {
'fadeIn': {'duration': 0.5},
'fadeOut': {'duration': 0.5}
},
'zIndex': 10,
'loop': False
}
]
}
)
response.raise_for_status()
data = response.json()
return data['videoId']
def add_timed_pip(main_video_url, pip_video_url, start_time, end_time):
"""Add PiP with timing control"""
response = requests.post(
'https://api.videocascade.com/v1/videos',
headers={
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
json={
'fileUrl': main_video_url,
'elements': [
{
'type': 'video',
'url': pip_video_url,
'timing': {'startTime': start_time, 'endTime': end_time},
'position': {'anchor': 'bottom-right'},
'size': {'width': '25%', 'fit': 'cover'},
'effects': {
'fadeIn': {'duration': 0.5},
'fadeOut': {'duration': 0.5}
},
'zIndex': 10
}
]
}
)
response.raise_for_status()
return response.json()['videoId']
# Usage
video_id = add_picture_in_picture(
'https://example.com/screen-recording.mp4',
'https://example.com/webcam.mp4'
)
# Show PiP only from 30s to 90s
add_timed_pip(main_video_url, pip_video_url, 30, 90)interface VideoElement {
type: 'video';
url: string;
timing: { entireVideo?: boolean; startTime?: number; endTime?: number };
position: { anchor: string };
size: { width: string; fit: string };
effects?: {
fadeIn?: { duration: number };
fadeOut?: { duration: number };
};
zIndex: number;
loop?: boolean;
}
async function addPictureInPicture(
mainVideoUrl: string,
pipVideoUrl: string
): Promise<string> {
const pipElement: VideoElement = {
type: 'video',
url: pipVideoUrl,
timing: { entireVideo: true },
position: { anchor: 'bottom-right' },
size: {
width: '25%',
fit: 'cover'
},
effects: {
fadeIn: { duration: 0.5 },
fadeOut: { duration: 0.5 }
},
zIndex: 10,
loop: false
};
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: mainVideoUrl,
elements: [pipElement]
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.videoId;
}
async function addTimedPiP(
mainVideoUrl: string,
pipVideoUrl: string,
startTime: number,
endTime: number
): Promise<string> {
const pipElement: VideoElement = {
type: 'video',
url: pipVideoUrl,
timing: { startTime, endTime },
position: { anchor: 'bottom-right' },
size: { width: '25%', fit: 'cover' },
effects: {
fadeIn: { duration: 0.5 },
fadeOut: { duration: 0.5 }
},
zIndex: 10
};
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: mainVideoUrl,
elements: [pipElement]
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return (await response.json()).videoId;
}
// Usage
const videoId = await addPictureInPicture(
'https://example.com/screen-recording.mp4',
'https://example.com/webcam.mp4'
);
// Show PiP only from 30s to 90s
await addTimedPiP(mainVideoUrl, pipVideoUrl, 30, 90);Multiple Overlays with Z-Index
Layer multiple overlays with proper stacking order.
async function addMultipleOverlays(videoUrl) {
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
elements: [
// Background watermark (bottom layer)
{
type: 'image',
url: 'https://example.com/background-pattern.png',
timing: { entireVideo: true },
position: { anchor: 'center' },
size: { width: '100%' },
effects: { opacity: 0.1 },
zIndex: 1
},
// Main logo (middle layer)
{
type: 'image',
url: 'https://example.com/logo.png',
timing: { entireVideo: true },
position: { anchor: 'top-right' },
size: { width: '12%' },
effects: { opacity: 0.9 },
zIndex: 50
},
// Watermark text (top layer)
{
type: 'image',
url: 'https://example.com/watermark-text.png',
timing: { entireVideo: true },
position: { anchor: 'bottom-right' },
size: { width: '15%' },
effects: { opacity: 0.7 },
zIndex: 100
},
// Opening title card (temporary, highest layer)
{
type: 'image',
url: 'https://example.com/title-card.png',
timing: { startTime: 0, endTime: 5 },
position: { anchor: 'center' },
size: { width: '60%' },
effects: {
fadeIn: { duration: 0.5 },
fadeOut: { duration: 0.5 }
},
zIndex: 200
},
// Background music
{
type: 'audio',
url: 'https://example.com/background-music.mp3',
timing: { entireVideo: true },
effects: {
volume: 0.2,
fadeIn: { duration: 2 },
fadeOut: { duration: 3 }
},
loop: true
}
]
}),
});
const data = await response.json();
return data.videoId;
}
// Usage
const videoId = await addMultipleOverlays('https://example.com/video.mp4');
console.log('Processing video with multiple overlays:', videoId);def add_multiple_overlays(video_url):
"""Add multiple layered overlays"""
response = requests.post(
'https://api.videocascade.com/v1/videos',
headers={
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
json={
'fileUrl': video_url,
'elements': [
# Background watermark (bottom layer)
{
'type': 'image',
'url': 'https://example.com/background-pattern.png',
'timing': {'entireVideo': True},
'position': {'anchor': 'center'},
'size': {'width': '100%'},
'effects': {'opacity': 0.1},
'zIndex': 1
},
# Main logo (middle layer)
{
'type': 'image',
'url': 'https://example.com/logo.png',
'timing': {'entireVideo': True},
'position': {'anchor': 'top-right'},
'size': {'width': '12%'},
'effects': {'opacity': 0.9},
'zIndex': 50
},
# Watermark text (top layer)
{
'type': 'image',
'url': 'https://example.com/watermark-text.png',
'timing': {'entireVideo': True},
'position': {'anchor': 'bottom-right'},
'size': {'width': '15%'},
'effects': {'opacity': 0.7},
'zIndex': 100
},
# Opening title card (temporary, highest layer)
{
'type': 'image',
'url': 'https://example.com/title-card.png',
'timing': {'startTime': 0, 'endTime': 5},
'position': {'anchor': 'center'},
'size': {'width': '60%'},
'effects': {
'fadeIn': {'duration': 0.5},
'fadeOut': {'duration': 0.5}
},
'zIndex': 200
},
# Background music
{
'type': 'audio',
'url': 'https://example.com/background-music.mp3',
'timing': {'entireVideo': True},
'effects': {
'volume': 0.2,
'fadeIn': {'duration': 2},
'fadeOut': {'duration': 3}
},
'loop': True
}
]
}
)
response.raise_for_status()
data = response.json()
return data['videoId']
# Usage
video_id = add_multiple_overlays('https://example.com/video.mp4')
print(f"Processing video with multiple overlays: {video_id}")type Element = ImageElement | AudioElement;
async function addMultipleOverlays(videoUrl: string): Promise<string> {
const elements: Element[] = [
// Background watermark (bottom layer)
{
type: 'image',
url: 'https://example.com/background-pattern.png',
timing: { entireVideo: true },
position: { anchor: 'center' },
size: { width: '100%' },
effects: { opacity: 0.1 },
zIndex: 1
},
// Main logo (middle layer)
{
type: 'image',
url: 'https://example.com/logo.png',
timing: { entireVideo: true },
position: { anchor: 'top-right' },
size: { width: '12%' },
effects: { opacity: 0.9 },
zIndex: 50
},
// Watermark text (top layer)
{
type: 'image',
url: 'https://example.com/watermark-text.png',
timing: { entireVideo: true },
position: { anchor: 'bottom-right' },
size: { width: '15%' },
effects: { opacity: 0.7 },
zIndex: 100
},
// Opening title card (temporary, highest layer)
{
type: 'image',
url: 'https://example.com/title-card.png',
timing: { startTime: 0, endTime: 5 },
position: { anchor: 'center' },
size: { width: '60%' },
effects: {
fadeIn: { duration: 0.5 },
fadeOut: { duration: 0.5 }
},
zIndex: 200
},
// Background music
{
type: 'audio',
url: 'https://example.com/background-music.mp3',
timing: { entireVideo: true },
effects: {
volume: 0.2,
fadeIn: { duration: 2 },
fadeOut: { duration: 3 }
},
loop: true
}
];
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
elements
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.videoId;
}
// Usage
const videoId = await addMultipleOverlays('https://example.com/video.mp4');
console.log('Processing video with multiple overlays:', videoId);Animated GIF Reactions
Add animated GIF stickers and reactions at specific moments.
async function addGifReaction(videoUrl, gifUrl, startTime, duration = 3) {
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
elements: [
{
type: 'gif',
url: gifUrl,
timing: {
startTime,
endTime: startTime + duration
},
position: { anchor: 'center' },
size: { width: '30%' },
effects: {
fadeIn: { duration: 0.3 },
fadeOut: { duration: 0.3 }
},
zIndex: 50,
loop: true
}
]
}),
});
const data = await response.json();
return data.videoId;
}
// Add multiple GIF reactions at different times
async function addMultipleGifReactions(videoUrl) {
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
elements: [
// Celebration at 15 seconds
{
type: 'gif',
url: 'https://example.com/celebration.gif',
timing: { startTime: 15, endTime: 18 },
position: { anchor: 'top-center' },
size: { width: '25%' },
effects: {
fadeIn: { duration: 0.3 },
fadeOut: { duration: 0.3 }
},
zIndex: 50,
loop: true
},
// Thumbs up at 45 seconds
{
type: 'gif',
url: 'https://example.com/thumbs-up.gif',
timing: { startTime: 45, endTime: 48 },
position: { anchor: 'bottom-right' },
size: { width: '20%' },
effects: {
fadeIn: { duration: 0.3 },
fadeOut: { duration: 0.3 }
},
zIndex: 50,
loop: true
},
// Fire emoji at 60 seconds
{
type: 'gif',
url: 'https://example.com/fire.gif',
timing: { startTime: 60, endTime: 63 },
position: { anchor: 'center' },
size: { width: '35%' },
effects: {
fadeIn: { duration: 0.3 },
fadeOut: { duration: 0.3 }
},
zIndex: 50,
loop: true
}
]
}),
});
return (await response.json()).videoId;
}
// Usage
await addGifReaction(videoUrl, 'https://example.com/celebration.gif', 30);
await addMultipleGifReactions(videoUrl);def add_gif_reaction(video_url, gif_url, start_time, duration=3):
"""Add animated GIF reaction"""
response = requests.post(
'https://api.videocascade.com/v1/videos',
headers={
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
json={
'fileUrl': video_url,
'elements': [
{
'type': 'gif',
'url': gif_url,
'timing': {
'startTime': start_time,
'endTime': start_time + duration
},
'position': {'anchor': 'center'},
'size': {'width': '30%'},
'effects': {
'fadeIn': {'duration': 0.3},
'fadeOut': {'duration': 0.3}
},
'zIndex': 50,
'loop': True
}
]
}
)
response.raise_for_status()
data = response.json()
return data['videoId']
def add_multiple_gif_reactions(video_url):
"""Add multiple GIF reactions at different times"""
response = requests.post(
'https://api.videocascade.com/v1/videos',
headers={
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
json={
'fileUrl': video_url,
'elements': [
# Celebration at 15 seconds
{
'type': 'gif',
'url': 'https://example.com/celebration.gif',
'timing': {'startTime': 15, 'endTime': 18},
'position': {'anchor': 'top-center'},
'size': {'width': '25%'},
'effects': {
'fadeIn': {'duration': 0.3},
'fadeOut': {'duration': 0.3}
},
'zIndex': 50,
'loop': True
},
# Thumbs up at 45 seconds
{
'type': 'gif',
'url': 'https://example.com/thumbs-up.gif',
'timing': {'startTime': 45, 'endTime': 48},
'position': {'anchor': 'bottom-right'},
'size': {'width': '20%'},
'effects': {
'fadeIn': {'duration': 0.3},
'fadeOut': {'duration': 0.3}
},
'zIndex': 50,
'loop': True
},
# Fire emoji at 60 seconds
{
'type': 'gif',
'url': 'https://example.com/fire.gif',
'timing': {'startTime': 60, 'endTime': 63},
'position': {'anchor': 'center'},
'size': {'width': '35%'},
'effects': {
'fadeIn': {'duration': 0.3},
'fadeOut': {'duration': 0.3}
},
'zIndex': 50,
'loop': True
}
]
}
)
response.raise_for_status()
return response.json()['videoId']
# Usage
add_gif_reaction(video_url, 'https://example.com/celebration.gif', 30)
add_multiple_gif_reactions(video_url)interface GifElement {
type: 'gif';
url: string;
timing: { startTime: number; endTime: number };
position: { anchor: string };
size: { width: string };
effects?: {
fadeIn?: { duration: number };
fadeOut?: { duration: number };
};
zIndex: number;
loop: boolean;
}
async function addGifReaction(
videoUrl: string,
gifUrl: string,
startTime: number,
duration: number = 3
): Promise<string> {
const gifElement: GifElement = {
type: 'gif',
url: gifUrl,
timing: {
startTime,
endTime: startTime + duration
},
position: { anchor: 'center' },
size: { width: '30%' },
effects: {
fadeIn: { duration: 0.3 },
fadeOut: { duration: 0.3 }
},
zIndex: 50,
loop: true
};
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
elements: [gifElement]
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.videoId;
}
async function addMultipleGifReactions(videoUrl: string): Promise<string> {
const gifs: GifElement[] = [
// Celebration at 15 seconds
{
type: 'gif',
url: 'https://example.com/celebration.gif',
timing: { startTime: 15, endTime: 18 },
position: { anchor: 'top-center' },
size: { width: '25%' },
effects: {
fadeIn: { duration: 0.3 },
fadeOut: { duration: 0.3 }
},
zIndex: 50,
loop: true
},
// Thumbs up at 45 seconds
{
type: 'gif',
url: 'https://example.com/thumbs-up.gif',
timing: { startTime: 45, endTime: 48 },
position: { anchor: 'bottom-right' },
size: { width: '20%' },
effects: {
fadeIn: { duration: 0.3 },
fadeOut: { duration: 0.3 }
},
zIndex: 50,
loop: true
},
// Fire emoji at 60 seconds
{
type: 'gif',
url: 'https://example.com/fire.gif',
timing: { startTime: 60, endTime: 63 },
position: { anchor: 'center' },
size: { width: '35%' },
effects: {
fadeIn: { duration: 0.3 },
fadeOut: { duration: 0.3 }
},
zIndex: 50,
loop: true
}
];
const response = await fetch('https://api.videocascade.com/v1/videos', {
method: 'POST',
headers: {
'Authorization': 'Bearer vca_your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
elements: gifs
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return (await response.json()).videoId;
}
// Usage
await addGifReaction(videoUrl, 'https://example.com/celebration.gif', 30);
await addMultipleGifReactions(videoUrl);Real-World Use Case: Social Media Branding
Complete example for automatically branding social media content.
class SocialMediaBrander {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.videocascade.com/v1';
}
async brandVideoForPlatform(videoUrl, platform, brandAssets) {
const platformConfigs = {
youtube: {
aspectRatio: '16:9',
logoSize: '10%',
logoPosition: 'top-right',
watermarkSize: '12%',
musicVolume: 0.15,
},
instagram: {
aspectRatio: '1:1',
logoSize: '12%',
logoPosition: 'top-center',
watermarkSize: '15%',
musicVolume: 0.2,
},
tiktok: {
aspectRatio: '9:16',
logoSize: '15%',
logoPosition: 'top-center',
watermarkSize: '18%',
musicVolume: 0.25,
},
twitter: {
aspectRatio: '16:9',
logoSize: '10%',
logoPosition: 'top-right',
watermarkSize: '12%',
musicVolume: 0.15,
},
};
const config = platformConfigs[platform];
const elements = [
// Logo
{
type: 'image',
url: brandAssets.logoUrl,
timing: { entireVideo: true },
position: { anchor: config.logoPosition },
size: { width: config.logoSize },
effects: {
opacity: 0.9,
fadeIn: { duration: 0.5 },
},
zIndex: 100,
},
// Watermark
{
type: 'image',
url: brandAssets.watermarkUrl,
timing: { entireVideo: true },
position: { anchor: 'bottom-right' },
size: { width: config.watermarkSize },
effects: { opacity: 0.7 },
zIndex: 90,
},
];
// Add background music if provided
if (brandAssets.musicUrl) {
elements.push({
type: 'audio',
url: brandAssets.musicUrl,
timing: { entireVideo: true },
effects: {
volume: config.musicVolume,
fadeIn: { duration: 2 },
fadeOut: { duration: 3 },
},
loop: true,
});
}
// Add intro card if provided
if (brandAssets.introCardUrl) {
elements.push({
type: 'image',
url: brandAssets.introCardUrl,
timing: { startTime: 0, endTime: 3 },
position: { anchor: 'center' },
size: { width: '70%' },
effects: {
fadeIn: { duration: 0.5 },
fadeOut: { duration: 0.5 },
},
zIndex: 200,
});
}
// Add outro card if provided
if (brandAssets.outroCardUrl) {
// Get video duration first (requires separate API call)
const metadata = await this.getVideoMetadata(videoUrl);
elements.push({
type: 'image',
url: brandAssets.outroCardUrl,
timing: {
startTime: metadata.duration - 5,
endTime: metadata.duration,
},
position: { anchor: 'center' },
size: { width: '70%' },
effects: {
fadeIn: { duration: 0.5 },
fadeOut: { duration: 0.5 },
},
zIndex: 200,
});
}
const response = await fetch(`${this.baseUrl}/videos`, {
method: 'POST',
headers: {
Authorization: `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileUrl: videoUrl,
aspectRatio: config.aspectRatio,
resizeMode: 'cover',
normalizeAudio: true,
compressionQuality: 95,
elements,
webhookUrl: `https://yourapp.com/webhooks/branded-video/${platform}`,
}),
});
return await response.json();
}
async getVideoMetadata(videoUrl) {
// Placeholder - implement actual metadata fetching
return { duration: 120 };
}
}
// Usage
const brander = new SocialMediaBrander('vca_your_api_key');
const brandAssets = {
logoUrl: 'https://example.com/brand/logo.png',
watermarkUrl: 'https://example.com/brand/watermark.png',
musicUrl: 'https://example.com/brand/background-music.mp3',
introCardUrl: 'https://example.com/brand/intro-card.png',
outroCardUrl: 'https://example.com/brand/outro-card.png',
};
// Brand for different platforms
await brander.brandVideoForPlatform(videoUrl, 'youtube', brandAssets);
await brander.brandVideoForPlatform(videoUrl, 'instagram', brandAssets);
await brander.brandVideoForPlatform(videoUrl, 'tiktok', brandAssets);
await brander.brandVideoForPlatform(videoUrl, 'twitter', brandAssets);Best Practices
1. Optimize Overlay Assets
Prepare assets for best performance:
// Good practices
- Use PNG with transparency for logos/watermarks
- Compress images (TinyPNG, ImageOptim)
- Use appropriate resolution (don't use 4K for 10% overlay)
- Test opacity levels (0.7-0.9 works well for most cases)
// Image size recommendations
- Logos: 500-800px width
- Watermarks: 300-500px width
- Title cards: 1920px width (16:9) or 1080px (1:1)
- Background patterns: Match video resolution2. Layer with Z-Index Spacing
Leave room for future additions:
// Good: Spaced out z-indexes
elements: [
{ zIndex: 10 }, // Background layer
{ zIndex: 50 }, // Middle layer
{ zIndex: 100 }, // Top layer
{ zIndex: 200 }, // Temporary overlays
];
// Bad: Sequential z-indexes
elements: [{ zIndex: 1 }, { zIndex: 2 }, { zIndex: 3 }, { zIndex: 4 }];3. Balance Audio Levels
Set appropriate volumes:
// Recommended volumes
{
volume: 0.15; // Subtle background music
volume: 0.25; // Moderate background music
volume: 0.4; // Prominent background music
volume: 0.6; // Dominant audio overlay
}
// Always normalize when adding audio
normalizeAudio: true; // Balances all audio tracks4. Use Fade Effects
Professional transitions:
// Always fade in/out temporary elements
{
effects: {
fadeIn: { duration: 0.5, easing: 'ease-in' },
fadeOut: { duration: 0.5, easing: 'ease-out' }
}
}
// Longer fades for audio
{
effects: {
fadeIn: { duration: 2 },
fadeOut: { duration: 3 }
}
}5. Mobile-Friendly Positioning
Test on different aspect ratios:
// Safe positioning
position: {
anchor: 'bottom-right';
} // Clear, predictable
size: {
width: '15%';
} // Responsive percentage
// Avoid edge positioning for important content
// Keep 5-10% margin from edges for mobile safety