How to add a title card intro with Claude

A bright yellow screen slides up a big black two-line headline for 1.5 seconds, then cuts straight to the first shot.

Get this effect

  1. Switch to Code in your Claude Desktop App

  2. Install the skill (once)

    Show the prompt
    Install the Recharm video effects skill: download https://recharm-growth-staging.s3.us-east-2.amazonaws.com/website/downloads/recharm-video-effects.zip and unzip it into ~/.claude/skills
  3. Ask for the effect

    Add a title card intro to input.mp4
    Lines: ["Your skin,", "but brighter"]

Several videos, several effects, one ask

Put your clips in the folder, mention them with @, and ask for everything at once. Claude runs one tested script after another on each clip, then joins them.

Add a title card intro to @clip1.mp4, put a sale badge on @clip2.mp4, give @clip3.mp4 a VHS look, then join all three with crossfades.

Settings you can change

Ask Claude to change any of these, or edit them at the top of the script.

SettingDefaultWhat it does
LINES["Your skin,", "but brighter"]The headline, one entry per line.
CARD1.5Seconds the card plays before the footage.
BACKGROUND(255, 210, 0)Card color.
INK(20, 20, 20)Text color.
SIZE0.13Text height as a share of the frame width.
STAGGER0.3Seconds between one line arriving and the next.
RISE0.25Seconds each line takes to slide up and fade in.

Without the skill

Don’t want to install the skill? Paste this prompt into Claude Code and Claude writes the script itself.

Show the prompt
Put a 1.5 second title card in front of input.mp4: a solid yellow (255, 210, 0) 540x960 screen with "Your skin," over "but brighter" in black (20, 20, 20) Arial Black, text 70 pixels tall, the two lines 1.25 line heights apart and centered on the middle of the frame. The first line slides up 38 pixels into place and fades in from 0.1 to 0.35 seconds, the second from 0.4 to 0.65 seconds, both easing out. At 1.5 seconds cut straight to the clip, which plays from its start, so the output is 7.5 seconds at 30fps. Delay the audio by 1.5 seconds so it stays in sync. Use Python with Pillow to draw the card (don't rely on ffmpeg's drawtext filter) and ffmpeg to join it to the clip, and save it as output.mp4.

Run it without Claude

This is the exact script we tested. Save it as title-card-intro.py and run:

python3 title-card-intro.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Title card intro: a yellow screen with a big black two-line headline plays for 1.5 seconds before the clip.

Usage: python3 title-card-intro.py input.mp4 output.mp4
The output is 1.5 seconds longer than the input; the sound waits for the card so it stays in sync.
Needs ffmpeg and Pillow (pip install pillow).
"""
import json
import subprocess
import sys
from pathlib import Path

from PIL import Image, ImageDraw, ImageFont

LINES = ["Your skin,", "but brighter"]  # headline, one entry per line
CARD = 1.5                # seconds the title card plays before the footage
BACKGROUND = (255, 210, 0)  # card color
INK = (20, 20, 20)        # text color
SIZE = 0.13               # text height as a share of the frame width
STAGGER = 0.3             # seconds between one line arriving and the next
RISE = 0.25               # seconds each line takes to slide up and fade in
FONTS = ["/System/Library/Fonts/Supplemental/Arial Black.ttf",    # macOS
         "/System/Library/Fonts/Supplemental/Arial Bold.ttf",     # macOS
         "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",  # Linux
         "C:/Windows/Fonts/ariblk.ttf"]                           # Windows
FONT_FILE = next((f for f in FONTS if Path(f).exists()), None) or sys.exit("No bold font found: add one to FONTS")


def card(t):
    """The card at t seconds: each line slides up 4% of the frame and fades in, STAGGER seconds after the last."""
    img = Image.new("RGB", (w, h), BACKGROUND)
    font = ImageFont.truetype(FONT_FILE, round(w * SIZE))
    step = font.size * 1.25
    for i, line in enumerate(LINES):
        p = min(max((t - 0.1 - i * STAGGER) / RISE, 0), 1)
        if p == 0:
            continue
        p = 1 - (1 - p) ** 3  # ease out
        layer = Image.new("RGBA", (w, h))
        y = h / 2 + (i - (len(LINES) - 1) / 2) * step + (1 - p) * h * 0.04
        ImageDraw.Draw(layer).text((w / 2, y), line, font=font, anchor="mm", fill=INK + (round(255 * p),))
        img.paste(layer, mask=layer)
    return img


src, out = sys.argv[1:3]
info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-show_entries", "stream=codec_type,width,height",
                                           "-of", "json", src]))
video = next(s for s in info["streams"] if s["codec_type"] == "video")
w, h = video["width"], video["height"]
has_audio = any(s["codec_type"] == "audio" for s in info["streams"])

# the card frames come in on stdin, then the clip is joined on after them
graph = "[1:v]format=yuv420p,setsar=1[c];[0:v]fps=30,format=yuv420p,setsar=1[v];[c][v]concat=n=2:v=1:a=0[out]"
maps = ["-map", "[out]"]
if has_audio:
    graph += f";[0:a]adelay={round(CARD * 1000)}:all=1[a]"
    maps += ["-map", "[a]"]
ff = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-i", src,
                       "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{w}x{h}", "-r", "30", "-i", "-",
                       "-filter_complex", graph, *maps, "-c:v", "libx264", "-crf", "18", "-c:a", "aac", out],
                      stdin=subprocess.PIPE)
for i in range(round(CARD * 30)):
    ff.stdin.write(card(i / 30).tobytes())
ff.stdin.close()
if ff.wait():
    sys.exit("ffmpeg failed")

Find 3x winning ads

Recharm cuts your ad footage into tagged, searchable clips: hooks, testimonials, product shots. Find the shots that win, add the effect with Claude, and ship 3x more ad edits.

Start 14-day free trial