How to add fireflies with Claude

The shot sinks into a blue dusk and about thirty yellow-green fireflies wander through it, each glowing up and going dark again on its own rhythm.

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 fireflies to input.mp4
    Second the scene starts to dim and the fireflies appear, and how many seconds the dimming takes: 0.5, 0.6
    How much red, green and blue the shot keeps once dimmed: a blue dusk: (0.38, 0.42, 0.58)

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 fireflies 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
START, FADE0.5, 0.6Second the scene starts to dim and the fireflies appear, and how many seconds the dimming takes.
NIGHT(0.38, 0.42, 0.58)How much red, green and blue the shot keeps once dimmed: a blue dusk.
COUNT32Number of fireflies.
CORE(3.0, 5.0)Core radius of the smallest and largest fireflies, in pixels.
PULSE(1.6, 2.8)Shortest and longest on-off cycle, in seconds.
WANDER70How far each firefly strays from its path, in pixels.
CORE_COLOR, GLOW_COLOR(245, 255, 175), (150, 235, 40)Color of each firefly's bright middle and of its halo.
SEED5Change for a different swarm.

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 fireflies to input.mp4 (540x960). Leave the frames before 0.5 seconds untouched. From 0.5 seconds, over 0.6 seconds, dim the shot to a blue dusk by multiplying red by 0.38, green by 0.42 and blue by 0.58, and keep it dimmed to the end. Scatter 32 fireflies at random over the whole frame. Each is a Gaussian dot with a core radius between 3.0 and 5.0 pixels in pale yellow-green (245, 255, 175), at 1.6x strength so the middle is solid, plus a halo 3.5 times as wide at 0.8x strength in firefly green (150, 235, 40); clip each layer to 1. Each firefly drifts in a random direction at 8 to 20 pixels per second, wanders up to 70 pixels side to side on a 5 to 9 second loop and 49 pixels up and down on a loop 0.6 times as long, and wraps round the frame edges. Each pulses on and off on its own cycle of 1.6 to 2.8 seconds with a random phase: brightness = max(0, sin(2*pi*(t - 0.5)/cycle + phase))^1.5, so it is dark for about half of each cycle. Fade the fireflies in with the dimming and add them with a screen blend so they only brighten. Use Python with numpy and ffmpeg (don't use ffmpeg's drawtext filter for anything), keep the audio, and save it as output.mp4.

Run it without Claude

This is the exact script we tested. Save it as fireflies.py and run:

python3 fireflies.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Fireflies: the shot dims to a blue dusk and yellow-green fireflies wander through it, each glowing on and off.

Usage: python3 fireflies.py input.mp4 output.mp4
Needs ffmpeg and numpy (pip install numpy).
"""
import json
import subprocess
import sys

import numpy as np

START = 0.5                   # second the scene starts to dim and the fireflies appear
FADE = 0.6                    # seconds the dimming takes
NIGHT = (0.38, 0.42, 0.58)    # how much red, green and blue the shot keeps once dimmed: a blue dusk
COUNT = 32                    # number of fireflies
CORE = (3.0, 5.0)             # core radius of the smallest and largest fireflies, in pixels
PULSE = (1.6, 2.8)            # shortest and longest on-off cycle, in seconds
WANDER = 70                   # how far each firefly strays from its path, in pixels
CORE_COLOR = (245, 255, 175)  # the bright middle of each firefly: pale yellow-green
GLOW_COLOR = (150, 235, 40)   # the halo round it: firefly green
SEED = 5                      # change for a different swarm


def probe(path):
    info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
                                               "stream=width,height,r_frame_rate", "-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)


def swarm(t):
    """The light layer at time t (0-1 per channel): each lit firefly a bright core inside a wide green glow."""
    core = np.zeros((h, w), np.float32)
    glow = np.zeros((h, w), np.float32)
    for f in flies:
        # a slow drift plus two loops of wandering, wrapping round the frame edges
        x = (f["x"] + f["vx"] * t + WANDER * np.sin(2 * np.pi * t / f["loop"] + f["phase"])) % w
        y = (f["y"] + f["vy"] * t + WANDER * 0.7 * np.sin(2 * np.pi * t / (f["loop"] * 0.6) + f["phase"] * 2)) % h
        # on for about half of each cycle, rising and falling smoothly, dark for the rest
        pulse = max(0.0, np.sin(2 * np.pi * (t - START) / f["pulse"] + f["phase"])) ** 1.5
        if pulse < 0.01:
            continue
        r = int(f["core"] * 7) + 1
        x0, y0, x1, y1 = max(int(x) - r, 0), max(int(y) - r, 0), min(int(x) + r + 1, w), min(int(y) + r + 1, h)
        yy, xx = np.mgrid[y0:y1, x0:x1].astype(np.float32)
        d2 = ((xx - x) ** 2 + (yy - y) ** 2) / f["core"] ** 2
        core[y0:y1, x0:x1] += pulse * 1.6 * np.exp(-d2)
        glow[y0:y1, x0:x1] += pulse * 0.8 * np.exp(-d2 / 12)  # a halo three and a half times as wide as the core
    light = np.minimum(core, 1)[..., None] * CORE_COLOR + np.minimum(glow, 1)[..., None] * GLOW_COLOR
    return np.minimum(light / 255, 1)


src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
rng = np.random.default_rng(SEED)
flies = []
for _ in range(COUNT):
    angle = rng.uniform(0, 2 * np.pi)
    speed = rng.uniform(8, 20)
    flies.append({"x": rng.uniform(0, w), "y": rng.uniform(0, h),
                  "vx": speed * np.cos(angle), "vy": speed * np.sin(angle),
                  "core": rng.uniform(*CORE), "loop": rng.uniform(5, 9),
                  "pulse": rng.uniform(*PULSE), "phase": rng.uniform(0, 2 * np.pi)})

reader = subprocess.Popen(["ffmpeg", "-v", "error", "-i", src, "-f", "rawvideo", "-pix_fmt", "rgb24", "-"],
                          stdout=subprocess.PIPE)
writer = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{w}x{h}",
                           "-r", rate, "-i", "-", "-i", src, "-map", "0:v", "-map", "1:a?",
                           "-c:v", "libx264", "-crf", "16", "-pix_fmt", "yuv420p", "-c:a", "copy", "-shortest", out],
                          stdin=subprocess.PIPE)
i = 0
while raw := reader.stdout.read(w * h * 3):
    img = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
    t = i / fps
    fade = np.clip((t - START) / FADE, 0, 1)
    if t >= START:
        dimmed = img * (1 - (1 - np.array(NIGHT)) * fade)
        light = swarm(t) * fade
        img = (255 - (255 - dimmed) * (1 - light)).astype(np.uint8)  # screen blend: the fireflies only brighten
    writer.stdin.write(img.tobytes())
    i += 1
writer.stdin.close()
if reader.wait() or writer.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