How to add rolling credits with Claude

The clip fades to black, then gray roles and white names glide up from the bottom of the screen at an even pace, like a film's end credits.

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 rolling credits to input.mp4
    Credits: [("Directed by", "Maya Chen"), ...]

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 rolling credits 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
CREDITS[("Directed by", "Maya Chen"), ...]The (role, name) pairs, top to bottom.
FADE_START1.0Second the clip starts fading to black.
FADE0.5Seconds the fade takes.
ROLL_START1.5Second the first line enters at the bottom.
SPEED200Pixels the credits move up each second.
ROLE_SIZE, NAME_SIZE0.042, 0.068Text heights as a share of the frame width.
ROLE_COLOR(160, 160, 160)Color of the small role line.
NAME_COLOR(255, 255, 255)Color of the name line.
GAP1.6Space between entries, in name heights.

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
Add rolling end credits to input.mp4 (540x960). Play the clip untouched until 1.0 seconds, then fade it to solid black between 1.0 and 1.5 seconds and keep it black to the end. From 1.5 seconds, scroll five centered credits up from the bottom edge at a steady 200 pixels per second: "Directed by" / "Maya Chen", "Starring" / "The Glow Serum", "Cinematography" / "Leo Park", "Music" / "The Late Hours", "Edited with" / "Claude". Each role is in capitals, 23 pixels tall, gray (160, 160, 160), with its name 34 pixels below it in white (255, 255, 255) Arial Bold 37 pixels tall; entries start 131 pixels apart. The top of the first role line is at the bottom edge (y = 960) at 1.5 seconds and 200 pixels higher each second after, so at 6 seconds all five credits are on screen. Draw the credits once on a tall transparent image and move it each frame. Use Python with Pillow to draw the text (don't rely on ffmpeg's drawtext filter) and ffmpeg to put it on the video, keep the audio and the 6 second length at 30fps, and save it as output.mp4.

Run it without Claude

This is the exact script we tested. Save it as rolling-credits.py and run:

python3 rolling-credits.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Rolling credits: the clip fades to black and white names scroll steadily up the screen, like the end of a film.

Usage: python3 rolling-credits.py input.mp4 output.mp4
Needs ffmpeg and Pillow (pip install pillow).
"""
import json
import math
import subprocess
import sys
from pathlib import Path

from PIL import Image, ImageDraw, ImageFont

CREDITS = [("Directed by", "Maya Chen"),       # (role, name) pairs, top to bottom
           ("Starring", "The Glow Serum"),
           ("Cinematography", "Leo Park"),
           ("Music", "The Late Hours"),
           ("Edited with", "Claude")]
FADE_START = 1.0          # second the clip starts fading to black
FADE = 0.5                # seconds the fade takes
ROLL_START = 1.5          # second the first line enters at the bottom
SPEED = 200               # pixels the credits move up each second
ROLE_SIZE, NAME_SIZE = 0.042, 0.068  # text heights as a share of the frame width
ROLE_COLOR = (160, 160, 160)  # small gray role line
NAME_COLOR = (255, 255, 255)  # big white name line
GAP = 1.6                 # space between entries, in name heights
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 credits_strip():
    """Every credit drawn once on a tall transparent strip as wide as the frame, centered."""
    role_font = ImageFont.truetype(FONT_FILE, round(w * ROLE_SIZE))
    name_font = ImageFont.truetype(FONT_FILE, round(w * NAME_SIZE))
    entry = role_font.size * 1.5 + name_font.size * (1 + GAP)
    strip = Image.new("RGBA", (w, math.ceil(entry * len(CREDITS))))
    draw = ImageDraw.Draw(strip)
    for i, (role, name) in enumerate(CREDITS):
        y = i * entry
        draw.text((w / 2, y), role.upper(), font=role_font, anchor="mt", fill=ROLE_COLOR)
        draw.text((w / 2, y + role_font.size * 1.5), name, font=name_font, anchor="mt", fill=NAME_COLOR)
    return strip


def overlay(t):
    """Black fading in over the clip, with the strip's top edge SPEED pixels higher every second after ROLL_START."""
    fade = min(max((t - FADE_START) / FADE, 0), 1)
    img = Image.new("RGBA", (w, h), (0, 0, 0, round(255 * fade)))
    if t >= ROLL_START:
        img.alpha_composite(strip, (0, round(h - (t - ROLL_START) * SPEED)))  # Pillow clips what falls off the frame
    return img


def probe(path):
    info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
                                               "stream=width,height,r_frame_rate:format=duration", "-of", "json", path]))
    s = info["streams"][0]
    num, den = s["r_frame_rate"].split("/")
    return s["width"], s["height"], s["r_frame_rate"], float(num) / float(den), float(info["format"]["duration"])


def render(src, out):
    """Draw overlay(t) on every frame of src and save to out, keeping the audio."""
    ff = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-i", src,
                           "-f", "rawvideo", "-pix_fmt", "rgba", "-s", f"{w}x{h}", "-r", rate, "-i", "-",
                           "-filter_complex", "[0:v][1:v]overlay=shortest=1,format=yuv420p[v]",
                           "-map", "[v]", "-map", "0:a?", "-c:v", "libx264", "-crf", "18", "-c:a", "copy", out],
                          stdin=subprocess.PIPE)
    for i in range(math.ceil(duration * fps)):
        ff.stdin.write(overlay(i / fps).tobytes())
    ff.stdin.close()
    if ff.wait():
        sys.exit("ffmpeg failed")


src, out = sys.argv[1:3]
w, h, rate, fps, duration = probe(src)
strip = credits_strip()
render(src, out)

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