How to add a day counter with Claude

A dark card at the top counts "DAY 1", "DAY 7" and "DAY 30" while the clip turns from dull to vivid and a yellow timeline along the bottom fills to each day.

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

    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.

SettingDefaultWhat 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.
POP0.3Seconds each day label takes to pop in.
FILL0.4Seconds the timeline takes to fill to the next day.
LABEL_Y0.12Label center, down the frame (0-1).
LINE_Y0.88Timeline 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.mp4
Show 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)

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