Instagram Reels & Carousel ad creation with Remotion. Use when creating vertical videos (9:16) for Instagram Reels/Stories or carousel posts (4:5)...
Complete toolkit for creating professional Instagram Reels and Carousel ads with Remotion.
Before creating any videos, configure your brand:
rules/design-system-template.md to rules/design-system.mdnode scripts/generate-backgrounds.jsnode scripts/scan-instagram-assets.jsSee rules/setup.md for complete project setup.
| File | Description |
|---|---|
| rules/setup.md | Initial project setup, dependencies, folder structure |
| rules/design-system-template.md | Template for your brand colors, fonts, and assets |
| rules/formats.md | Instagram display formats (9:16, 4:5, 1:1) and crop zones |
| File | Description |
|---|---|
| rules/voiceover.md | ElevenLabs integration, scene JSON, timing sync |
| rules/captions.md | Animated captions with word-level timing and highlighting |
| rules/animations.md | Spring configs, transitions, animation components |
| rules/components.md | Reusable template components for scenes |
| File | Description |
|---|---|
| rules/local-assets.md | Backgrounds, icons, illustrations management |
| rules/carousels.md | Instagram carousel design specs, batch rendering |
| Property | Value |
|---|---|
| Canvas | 1080Γ1920px (9:16 aspect ratio) |
| Resolution | Minimum 720p, optimal 1080p |
| Format | MP4 (H.264 codec) with AAC audio |
| Frame Rate | 30 FPS |
| Duration | 15-60 seconds (15s recommended for ads) |
| Property | Value |
|---|---|
| Canvas | 1080Γ1350px (4:5 aspect ratio) |
| Slides | 2-10 images per carousel |
| Format | PNG or JPEG |
βββββββββββββββββββββββββββββββββββββββ 0px
β TOP DANGER ZONE β
β (username, "Reels" branding, UI) β
β 250px buffer β
βββββββββββββββββββββββββββββββββββββββ€ ~285px
β βββββββββββββββββββββββββββββββ β
β β β β
β β OPTIMAL CONTENT β β
β β ZONE β β
β β β β
β β 880Γ1350px centered β β
β β (middle 60% of screen) β β
β β β β
β β Place logos, key text, β β
β β faces, CTAs here β β
β β β β
β βββββββββββββββββββββββββββββββ β
β β80px 120pxβ β
βββββββββββββββββββββββββββββββββββββββ€ ~1520px
β BOTTOM DANGER ZONE β
β (captions, buttons, audio, CTA) β
β 400px buffer β
β β οΈ MOST CRITICAL ZONE β οΈ β
βββββββββββββββββββββββββββββββββββββββ 1920px
export const INSTAGRAM_REELS = {
width: 1080,
height: 1920,
aspectRatio: "9:16",
fps: 30,
buffer: {
top: 250,
bottom: 400,
left: 80,
right: 120,
},
safeArea: {
x: 80,
y: 285,
width: 880,
height: 1235,
},
// Feed preview (4:5) crops
feedPreview: {
cropTop: 285,
cropBottom: 285,
},
// Grid preview (1:1) crops
gridPreview: {
cropTop: 420,
cropBottom: 420,
},
};
See rules/formats.md for detailed format specifications.
Create rules/design-system.md from the template:
// TODO: Replace with your brand values
const COLORS = {
primary: "#YOUR_PRIMARY_COLOR",
secondary: "#YOUR_SECONDARY_COLOR",
background: "#YOUR_BG_COLOR",
foreground: "#YOUR_TEXT_COLOR",
dark: "#YOUR_DARK_COLOR",
accent: "#YOUR_ACCENT_COLOR",
};
// TODO: Import your brand fonts
import { loadFont } from "@remotion/google-fonts/YourHeadingFont";
import { loadFont as loadBodyFont } from "@remotion/google-fonts/YourBodyFont";
const { fontFamily: headingFont } = loadFont();
const { fontFamily: bodyFont } = loadBodyFont();
// TODO: Set your logo path
const LOGO_PATH = "your-logo.png"; // In public/ folder
See rules/design-system-template.md for complete configuration.
| Scene | Purpose | Duration | Character |
|---|---|---|---|
| Scene 1: Hook | Grab attention | 2-4s | dramatic |
| Scene 2: Problem | Establish pain point | 3-5s | narrator |
| Scene 3: Solution | Present answer | 3-5s | expert |
| Scene 4: CTA | Call to action | 2-4s | calm |
# Generate voiceover with timestamps
node tools/generate.js \
--scenes remotion/instagram-ads/scenes/ad-example-scenes.json \
--with-timestamps \
--output-dir public/audio/instagram-ads/ad-example/
# With pronunciation dictionary for brand names
node tools/generate.js \
--scenes remotion/instagram-ads/scenes/ad-example-scenes.json \
--dictionary your-brand \
--with-timestamps \
--output-dir public/audio/instagram-ads/ad-example/
Create custom dictionaries in dictionaries/ for correct brand name pronunciation:
<!-- dictionaries/your-brand.pls -->
<lexeme>
<grapheme>YourBrand</grapheme>
<alias>Jor BrΓ€nd</alias>
</lexeme>
See dictionaries/template.pls for full format.
{
"name": "ad-example",
"voice": "YourVoiceName",
"character": "narrator",
"scenes": [
{
"id": "scene1",
"text": "Hook text here.",
"duration": 3.5,
"character": "dramatic"
},
{
"id": "scene2",
"text": "Problem description.",
"duration": 4.5
},
{
"id": "scene3",
"text": "Solution presentation.",
"duration": 4.0,
"character": "expert"
},
{
"id": "scene4",
"text": "Call to action. Brand Name.",
"duration": 3.0,
"character": "calm"
}
]
}
| Character | Style | Best For |
|---|---|---|
dramatic |
Intense, emotional | Hooks, problem statements |
narrator |
Professional, smooth | General content |
expert |
Authoritative | Solutions, legal content |
calm |
Soothing, reassuring | CTAs, trust-building |
salesperson |
Enthusiastic | Marketing, ads |
See rules/voiceover.md for complete integration guide.
Generate word-level timestamps for animated captions:
node tools/generate.js \
--scenes scenes.json \
--with-timestamps \
--output-dir public/audio/ad-example/
Key features:
See rules/captions.md for implementation.
| Element | Size | Weight |
|---|---|---|
| Hero headline | 64-80px | 700 |
| Section headline | 52-64px | 600 |
| Body/subtitle | 44-52px | 500 |
| Bullet points | 40-48px | 500 |
| Captions | 48-56px | 400 |
| CTA button | 36-48px | 600 |
| Fine print | 24-28px | 400 |
// High-contrast text (readable on any background)
const contrastTextStyle = {
color: "#ffffff",
textShadow: "0 2px 8px rgba(0,0,0,0.8), 0 0 20px rgba(0,0,0,0.5)",
};
// Highlighted word
<span style={{ color: COLORS.accent, fontWeight: 700 }}>
keyword
</span>
// Smooth - professional, no bounce
const SPRING_SMOOTH = { damping: 200 };
// Quick - snappy transitions
const SPRING_QUICK = { damping: 15, stiffness: 100 };
// Bouncy - attention-grabbing
const SPRING_BOUNCY = { damping: 8, stiffness: 200 };
| Animation | Use For |
|---|---|
| Fade in + slide up | Text reveals |
| Scale pop | Icons, logos |
| Staggered list | Bullet points |
| Crossfade | Scene transitions |
See rules/animations.md for all animation patterns.
public/
βββ images/
β βββ instagram-ads/
β βββ backgrounds/ # 1080Γ1920 or 1080Γ1350
β βββ icons/ # 64-256px elements
β βββ illustrations/ # 256-800px graphics
β β βββ ad-example/
β β βββ final/ # Processed with transparency
β βββ overlays/ # Transparent overlays
βββ audio/
β βββ instagram-ads/
β βββ ad-example/ # Per-ad voiceover files
β βββ ad-example-combined.mp3
β βββ ad-example-info.json
β βββ ad-example-captions.json
βββ your-logo.png
See rules/local-assets.md for asset management.
npx remotion render AdExample out/reel.mp4 \
--codec=h264 \
--crf=18 \
--audio-codec=aac \
--audio-bitrate=192k
# Batch render all slides
for i in 1 2 3 4 5; do
npx remotion still remotion/index.ts "Carousel-Slide$i" \
"public/images/carousels/example/slide$i.png" --overwrite
done
import React from "react";
import {
AbsoluteFill,
Audio,
Series,
staticFile,
useVideoConfig,
} from "remotion";
// TODO: Import from your design-system.md
const COLORS = {
primary: "#YOUR_PRIMARY",
secondary: "#YOUR_SECONDARY",
background: "#YOUR_BG",
dark: "#YOUR_DARK",
accent: "#YOUR_ACCENT",
};
export const AdTemplate: React.FC = () => {
const { fps } = useVideoConfig();
// Durations from voiceover info.json (actualDuration values)
const SCENE_DURATIONS = {
scene1: 3.5,
scene2: 4.5,
scene3: 4.0,
scene4: 3.0,
};
const paddingFrames = 5;
const scene1Frames = Math.round(SCENE_DURATIONS.scene1 * fps) + paddingFrames;
const scene2Frames = Math.round(SCENE_DURATIONS.scene2 * fps) + paddingFrames;
const scene3Frames = Math.round(SCENE_DURATIONS.scene3 * fps) + paddingFrames;
const totalTargetFrames = Math.round(15 * fps);
const scene4Frames = totalTargetFrames - scene1Frames - scene2Frames - scene3Frames;
return (
<AbsoluteFill>
<Audio src={staticFile("audio/instagram-ads/ad-example/ad-example-combined.mp3")} />
<Series>
<Series.Sequence durationInFrames={scene1Frames}>
<Scene1Hook />
</Series.Sequence>
<Series.Sequence durationInFrames={scene2Frames}>
<Scene2Problem />
</Series.Sequence>
<Series.Sequence durationInFrames={scene3Frames}>
<Scene3Solution />
</Series.Sequence>
<Series.Sequence durationInFrames={scene4Frames}>
<Scene4CTA />
</Series.Sequence>
</Series>
</AbsoluteFill>
);
};
See rules/components.md for scene template components.
# 1. Setup (one time)
cp rules/design-system-template.md rules/design-system.md
# Edit design-system.md with your brand values
cp dictionaries/template.pls dictionaries/your-brand.pls
# Edit your-brand.pls with brand pronunciations
node scripts/generate-backgrounds.js
node scripts/scan-instagram-assets.js
# 2. Create scenes JSON
vim remotion/instagram-ads/scenes/ad-new-scenes.json
# 3. Generate voiceover (with optional dictionary)
node tools/generate.js \
--scenes remotion/instagram-ads/scenes/ad-new-scenes.json \
--dictionary your-brand \
--with-timestamps \
--output-dir public/audio/instagram-ads/ad-new/
# 4. Create composition
# Use actualDuration values from ad-new-info.json
# 5. Preview
npx remotion studio
# 6. Render
npx remotion render AdNew out/ad-new.mp4 --codec=h264 --crf=18
# 7. Test on mobile before uploading
remotion-ads/
βββ SKILL.md # This file
βββ README.md # Quick start guide
βββ tools/
β βββ generate.js # ElevenLabs voiceover generator
βββ dictionaries/
β βββ template.pls # Dictionary template
β βββ example.pls # Example dictionary
βββ rules/
βββ setup.md
βββ voiceover.md # Voiceover & dictionary docs
βββ captions.md
βββ animations.md
βββ components.md
βββ formats.md
βββ local-assets.md
βββ carousels.md
βββ design-system-template.md