How to add an aurora with Claude

The backdrop above the bottle darkens to a starry night sky, and rippling curtains of light, green along their lower edge and purple above, sway across it.

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 an aurora to input.mp4
    Second the night sky and aurora start to fade in, and how many seconds the fade takes: 0.5, 1.0
    How far down the frame the night sky reaches (0-1): 0.60

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 an aurora 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, 1.0Second the night sky and aurora start to fade in, and how many seconds the fade takes.
SKY_END0.60How far down the frame the night sky reaches (0-1).
SKY(14, 20, 48)Night sky color at the very top.
GREEN, PURPLE(60, 255, 150), (170, 70, 255)Aurora color along each curtain's lower edge and at its top.
CURTAINS(0.17, 0.11)Height of each curtain's lower edge (0-1 down the frame).
SWAY0.6How fast the curtains wave, in cycles per second.
STARS70Number of stars in the sky.
SEED5Change for a different pattern of rays and stars.

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 a northern lights effect to input.mp4 with Python and numpy. From 0.5 seconds, fading in over 1 second and staying to the end, turn the top of the frame into a night sky: multiply each pixel by 0.12 + 0.88 * n and add (14, 20, 48) * (1 - n), where n = (y / (0.6 * height)) ** 2.5 capped at 1, so the very top is near-black navy and everything below 60% down is untouched. Scatter 70 single-pixel stars of brightness 120 to 230 in the top 42% of the frame, twinkling gently. Then screen-blend two aurora curtains over it. Each curtain has a wavy lower edge at 17% and 11% down the frame, moved by 0.035 * sin(2π * 1.3 * x/width + phase) + 0.015 * sin(2π * 3.1 * x/width - 1.4 * phase), with phase = 2π * 0.6 * t (offset 2.1 radians for the second curtain). Above the edge the light fades upward as exp(-distance / 0.16 of the height); below it, it cuts off within about 1% of the height. Streak it into vertical rays a few pixels wide (random noise smoothed over 9 columns, brightness 0.35 to 1) that slide up to 25 pixels sideways as the curtain waves. Color it green (60, 255, 150) at the lower edge, blending to purple (170, 70, 255) by 9% of the height above it; the lower curtain is at 0.9 brightness and the higher one at 0.65. Leave the frames before 0.5 seconds untouched. Use ffmpeg to read and write the video, don't use 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 aurora.py and run:

python3 aurora.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Aurora: the top of the frame dims to a starry night sky and waving green-to-purple curtains of light glow across it.

Usage: python3 aurora.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 night sky and aurora start to fade in
FADE = 1.0                   # seconds the fade-in takes
SKY_END = 0.60               # how far down the frame the night sky reaches (0-1)
SKY = (14, 20, 48)           # night sky color at the very top
GREEN = (60, 255, 150)       # aurora color along the lower edge of each curtain
PURPLE = (170, 70, 255)      # aurora color at the top of each curtain
CURTAINS = (0.17, 0.11)      # height of each curtain's lower edge (0-1 down the frame)
SWAY = 0.6                   # how fast the curtains wave, in cycles per second
STARS = 70                   # number of stars in the sky
SEED = 5                     # change for a different pattern of rays and stars


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 rays(rng, n):
    """Brightness (0.35-1) of the vertical light rays across n columns: random noise smoothed to rays a few pixels wide."""
    noise = np.convolve(rng.random(n * 3), np.ones(9) / 9, mode="same")[n:2 * n]
    noise = (noise - noise.min()) / (noise.max() - noise.min())
    return 0.35 + 0.65 * noise


def aurora(t):
    """The aurora's light at each pixel at time t (0-255 RGB), before fading in."""
    x = np.arange(w, dtype=np.float32) / w
    y = np.arange(h, dtype=np.float32)[:, None] / h
    light = np.zeros((h, w, 3), np.float32)
    for k, (base, ray) in enumerate(zip(CURTAINS, ray_sets)):
        phase = 2 * np.pi * SWAY * t + k * 2.1
        edge = base + 0.035 * np.sin(2 * np.pi * 1.3 * x + phase) + 0.015 * np.sin(2 * np.pi * 3.1 * x - 1.4 * phase)
        above = edge - y                                      # distance up from the curtain's lower edge
        glow = np.where(above >= 0, np.exp(-above / 0.16), np.exp(above / 0.012))  # tall fade up, sharp lower edge
        shift = round(25 * np.sin(phase * 0.7))               # rays slide sideways as the curtain waves
        bright = glow * np.roll(ray, shift)[None, :] * (0.9 - 0.25 * k)
        mix = np.clip(above / 0.09, 0, 1)[..., None]          # green at the edge, purple higher up
        light += bright[..., None] * (green * (1 - mix) + purple * mix)
    return light


src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
rng = np.random.default_rng(SEED)
ray_sets = [rays(rng, w) for _ in CURTAINS]
green, purple = np.array(GREEN, np.float32), np.array(PURPLE, np.float32)
down = np.clip(np.arange(h, dtype=np.float32) / (h * SKY_END), 0, 1)[:, None, None]
night = down ** 2.5                                            # 0 at the top (full night) to 1 at SKY_END (untouched)
sky = np.array(SKY, np.float32) * (1 - night)
stars = np.zeros((h, w), np.float32)
sy, sx = rng.integers(0, round(h * SKY_END * 0.7), STARS), rng.integers(0, w, STARS)
stars[sy, sx] = rng.uniform(120, 230, STARS)

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", "18", "-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).astype(np.float32)
    t = i / fps
    fade = np.clip((t - START) / FADE, 0, 1)
    if fade > 0:
        dark = img * (0.12 + 0.88 * night) + sky                   # dim the top into a night sky
        dark += stars[..., None] * (0.7 + 0.3 * np.sin(7 * t + stars[..., None]))  # twinkling stars
        light = aurora(t)
        dark = 255 - (255 - np.clip(dark, 0, 255)) * (255 - np.clip(light, 0, 255)) / 255  # screen the aurora on
        img = img * (1 - fade) + dark * fade
    writer.stdin.write(np.clip(img, 0, 255).astype(np.uint8).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