How to add a meme caption bar with Claude

The clip drops below a white strip across the top quarter of the frame, where a bold black caption reads like a meme setup line.

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 meme caption bar to input.mp4
    Text: Me after one week of using this serum

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 meme caption bar 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
TEXTMe after one week of using this serumThe caption.
BAR0.25Bar height as a share of the frame height.
SIZE0.08Text height as a share of the frame width.
PAD0.06Space either side of the text, as a share of the frame width.

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
Turn input.mp4 (540x960) into a meme-format video: a pure white (255, 255, 255) bar across the top 240 pixels holds the caption "Me after one week of using this serum" in black Arial Bold at 43 px, centered and wrapped to fit within 32 px of each side, with lines 1.2x the text size apart and the block centered in the bar. Below the bar, show the middle 540x720 of the video (crop 120 pixels off its top and bottom) so nothing is hidden behind the bar. Keep it at 540x960, 30fps, on screen the whole 6 seconds. Use Python with Pillow to draw the bar and ffmpeg to stack it on the video (don't rely on ffmpeg's drawtext filter), keep the audio, and save it as output.mp4.

Run it without Claude

This is the exact script we tested. Save it as meme-caption-bar.py and run:

python3 meme-caption-bar.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Meme caption bar: a white bar across the top holds a black meme caption, with the video cropped to fit underneath.

Usage: python3 meme-caption-bar.py input.mp4 output.mp4
Needs ffmpeg and Pillow (pip install pillow).
"""
import subprocess
import sys
import tempfile
from pathlib import Path

from PIL import Image, ImageDraw, ImageFont

TEXT = "Me after one week of using this serum"
BAR = 0.25           # bar height as a share of the frame height
SIZE = 0.08          # text height as a share of the frame width
PAD = 0.06           # space either side of the text, as a share of the frame width
FONTS = ["/System/Library/Fonts/Supplemental/Arial Bold.ttf",     # macOS
         "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",  # Linux
         "C:/Windows/Fonts/arialbd.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 wrap(text, font, width):
    lines, line = [], ""
    for word in text.split():
        trial = f"{line} {word}".strip()
        if font.getlength(trial) <= width or not line:
            line = trial
        else:
            lines.append(line)
            line = word
    return lines + [line]


def caption_bar(w, bar_h):
    """The white bar with the caption centered in it, black text, lines 1.2x the text size apart."""
    font = ImageFont.truetype(FONT_FILE, round(w * SIZE))
    lines = wrap(TEXT, font, w - 2 * round(w * PAD))
    step = round(font.size * 1.2)
    top = (bar_h - step * len(lines)) / 2
    img = Image.new("RGB", (w, bar_h), "white")
    draw = ImageDraw.Draw(img)
    for i, line in enumerate(lines):
        draw.text((w / 2, top + step * i + step / 2), line, font=font, anchor="mm", fill="black")
    return img


src, out = sys.argv[1:3]
w, h = map(int, subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
                                         "stream=width,height", "-of", "csv=p=0", src]).decode().split(","))
bar_h = round(h * BAR) // 2 * 2
with tempfile.TemporaryDirectory() as tmp:
    bar_png = f"{tmp}/bar.png"
    caption_bar(w, bar_h).save(bar_png)
    # Crop the middle of the video to the height left under the bar, then stack the bar on top.
    subprocess.run(["ffmpeg", "-y", "-v", "error", "-i", src, "-loop", "1", "-i", bar_png, "-filter_complex",
                    f"[0:v]crop={w}:{h - bar_h}[clip];[1:v][clip]vstack=shortest=1,fps=30,format=yuv420p[v]",
                    "-map", "[v]", "-map", "0:a?", "-c:v", "libx264", "-crf", "18", "-c:a", "copy", out], check=True)

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