Get this effect
Switch to Code in your Claude Desktop App
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/skillsAsk for the effect
Make a day counter from before.mp4 and after.mp4 Second each stage starts and the day number it shows: [(0.0, 1), (2.0, 7), (4.0, 30)] How much of the before clip shows at each stage; the rest is the after clip: [1.0, 0.5, 0.0]
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 day counter 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.
| Setting | Default | What it does |
|---|---|---|
STAGES | [(0.0, 1), (2.0, 7), (4.0, 30)] | Second each stage starts and the day number it shows. |
BEFORE_SHARE | [1.0, 0.5, 0.0] | How much of the before clip shows at each stage; the rest is the after clip. |
POP | 0.3 | Seconds each day label takes to pop in. |
FILL | 0.4 | Seconds the timeline takes to fill to the next day. |
LABEL_Y | 0.12 | Label center, down the frame (0-1). |
LINE_Y | 0.88 | Timeline height, down the frame (0-1). |
LINE_X | (0.15, 0.85) | Timeline start and end, across the frame (0-1). |
ACCENT | (255, 205, 0) | Color of the day number and the filled timeline. |
CARD | (20, 20, 24, 200) | Color and opacity of the cards behind the label and timeline. |
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
Make a day-by-day results video from before.mp4 (the dull look) and after.mp4 (the vivid look), both 540x960. From 0 to 2 seconds show before.mp4, from 2 to 4 seconds a 50/50 mix of the two, and from 4 to 6 seconds after.mp4, keeping before.mp4's audio. Each stage gets a label centered horizontally at 12% down the frame: a dark rounded card (RGB 20, 20, 24 at 80% opacity) reading "DAY" in white bold at 7.5% of the frame width tall, followed by the number in yellow (255, 205, 0) bold at 13% of the width: "DAY 1" at 0 seconds, "DAY 7" at 2 seconds and "DAY 30" at 4 seconds. Each label pops in over 0.3 seconds with a slight overshoot and replaces the one before. Along the bottom, at 88% down, draw a timeline from 15% to 85% across on its own dark rounded card: a thin translucent white line with three dots (at 15%, 50% and 85% across) captioned "DAY 1", "DAY 7" and "DAY 30" underneath. The line turns yellow (255, 205, 0) up to the current day's dot, filling over 0.4 seconds as each new day starts, reached dots are yellow and the current one has a yellow ring around it. Draw the labels and timeline with Python and Pillow (don't use ffmpeg's drawtext filter), composite them with ffmpeg, keep the audio and save it as output.mp4 (6 seconds, 30fps).Run it without Claude
This is the exact script we tested. Save it as day-counter.py and run:
python3 day-counter.py before.mp4 after.mp4 output.mp4Show the script
#!/usr/bin/env python3
"""Day counter: "DAY 1", "DAY 7" and "DAY 30" pop up in turn as the clip goes from dull to vivid, with a timeline filling in.
Usage: python3 day-counter.py before.mp4 after.mp4 output.mp4 (the result at its worst and at its best)
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
STAGES = [(0.0, 1), (2.0, 7), (4.0, 30)] # (second the stage starts, day number)
BEFORE_SHARE = [1.0, 0.5, 0.0] # how much of the before clip shows at each stage (the rest is the after clip)
POP = 0.3 # seconds each day label takes to pop in
FILL = 0.4 # seconds the timeline takes to fill to the next day
LABEL_Y = 0.12 # label center, down the frame (0-1)
LINE_Y = 0.88 # timeline height, down the frame (0-1)
LINE_X = (0.15, 0.85) # timeline start and end, across the frame (0-1)
ACCENT = (255, 205, 0) # yellow for the day number and the filled timeline
CARD = (20, 20, 24, 200) # dark card behind the label
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 label(day):
"""A dark rounded card reading DAY <n>, with the number in yellow."""
word_font, num_font = ImageFont.truetype(FONT_FILE, round(w * 0.075)), ImageFont.truetype(FONT_FILE, round(w * 0.13))
word, num = "DAY ", str(day)
tw, nw = word_font.getlength(word), num_font.getlength(num)
pad = w * 0.05
card = Image.new("RGBA", (round(tw + nw + 2 * pad), round(w * 0.2)))
draw = ImageDraw.Draw(card)
draw.rounded_rectangle([0, 0, card.width - 1, card.height - 1], radius=card.height // 4, fill=CARD)
base = card.height * 0.72
draw.text((pad, base), word, font=word_font, anchor="ls", fill="white")
draw.text((pad + tw, base), num, font=num_font, anchor="ls", fill=ACCENT)
return card
def timeline(img, reach, current):
"""A thin line with a dot per stage; yellow up to `reach` (0-1), the current day's dot ringed."""
draw = ImageDraw.Draw(img)
x0, x1, y = w * LINE_X[0], w * LINE_X[1], h * LINE_Y
thick, r = max(3, round(w * 0.008)), w * 0.022
small = ImageFont.truetype(FONT_FILE, round(w * 0.035))
draw.rounded_rectangle([x0 - r * 3, y - r * 2.5, x1 + r * 3, y + r * 4.8], radius=r * 2, fill=CARD)
draw.rounded_rectangle([x0, y - thick, x1, y + thick], radius=thick, fill=(255, 255, 255, 150))
draw.rounded_rectangle([x0, y - thick, x0 + (x1 - x0) * reach, y + thick], radius=thick, fill=ACCENT)
for i, (_, day) in enumerate(STAGES):
x = x0 + (x1 - x0) * i / (len(STAGES) - 1)
reached = i <= current
if i == current:
draw.ellipse([x - r * 1.5, y - r * 1.5, x + r * 1.5, y + r * 1.5], outline=ACCENT, width=thick)
draw.ellipse([x - r, y - r, x + r, y + r], fill=ACCENT if reached else (255, 255, 255, 170))
draw.text((x, y + r * 2.4), f"DAY {day}", font=small, anchor="mt", fill="white" if reached else (255, 255, 255, 170))
def overlay(t):
img = Image.new("RGBA", (w, h))
current = max(i for i, (start, _) in enumerate(STAGES) if t >= start)
start = STAGES[current][0]
step = 1 / (len(STAGES) - 1)
reach = step * (current - 1 + ease_out(min((t - start) / FILL, 1))) if current else 0
timeline(img, reach, current)
scale = ease_out_back((t - start) / POP)
if scale > 0.02:
card = cards[current]
c = card.resize((max(1, round(card.width * scale)), max(1, round(card.height * scale))), Image.LANCZOS)
img.alpha_composite(c, (round(w / 2 - c.width / 2), round(h * LABEL_Y - c.height / 2)))
return img
def ease_out(p):
return 1 - (1 - p) ** 3
def ease_out_back(p):
"""0 -> 1 with a small overshoot past 1 before settling, which reads as a 'pop'."""
p = min(max(p, 0), 1)
return 1 + 2.70158 * (p - 1) ** 3 + 1.70158 * (p - 1) ** 2
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(before, after, out):
"""Mix the two clips stage by stage, draw overlay(t) on every frame and save to out, keeping before's audio."""
share = str(BEFORE_SHARE[-1]) # becomes if(lt(T,2),1.0,if(lt(T,4),0.5,0.0)) for the defaults
for (next_start, _), b in reversed(list(zip(STAGES[1:], BEFORE_SHARE))):
share = f"if(lt(T,{next_start}),{b},{share})"
ff = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-i", before, "-i", after,
"-f", "rawvideo", "-pix_fmt", "rgba", "-s", f"{w}x{h}", "-r", rate, "-i", "-",
"-filter_complex", f"[1:v]scale={w}:{h},setsar=1[b];[0:v]setsar=1[a];"
f"[a][b]blend=all_expr='A*({share})+B*(1-({share}))'[mix];"
"[mix][2: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")
before, after, out = sys.argv[1:4]
w, h, rate, fps, duration = probe(before)
cards = [label(day) for _, day in STAGES]
render(before, after, out)





