How to add a warp speed with Claude

The shot fades to a dark blue void full of white star dots, the stars speed up and stretch into long streaks bursting out from the center, and a white flash drops you back into the video.

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 a warp speed to input.mp4
    Color the picture fades to: (2, 4, 18)
    How far toward the void it fades (1 = the video is gone): 0.9

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 warp speed 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
VOID(2, 4, 18)Color the picture fades to.
DARK0.9How far toward the void it fades (1 = the video is gone).
STARS_FROM1.0Second the stars appear, as still dots.
STARS450Stars in the field. More reads as denser space.
SPEED(0.12, 2.6)Star speed at the start and at full warp.
RAMP(1.4, 2.9)Seconds the stars speed up between.
TRAIL0.12Streak length, in seconds of travel. Raise it for longer streaks.
GLOW(120, 170, 255)Color of the glow around the streaks.
FLASH(3.1, 3.4, 3.8)White flash: when it starts, peaks and has faded back to the video.

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 warp speed (hyperspace) effect to input.mp4. From 0.8s to 1.1s, fade the picture 90% of the way to a dark blue void (2, 4, 18) with a smooth ease. From 1.0s, draw a 3D star field over it with Python, numpy and Pillow: 450 stars, each at a random x within ±60% of the frame width and y within ±60% of the frame height (pixels, as seen at depth 1) and a random depth z from 0.15 to 1.0, shown on screen at (width/2 + x/z, height/2 + y/z). Each frame, move every star toward the viewer by speed/30 in z; a star whose z drops below 0.05 respawns at a new random x, y with z = 1. The speed is 0.12 per second until 1.4s, then eases (smoothstep) up to 2.6 per second by 2.9s. Draw each star as a pure white (255, 255, 255) line from where it was 0.12 seconds of travel ago (z + speed x 0.12, capped at 1) to where it is now, with a round dot at the head, 1 pixel wide for the far stars and up to 3.5 pixels for near ones, brightness from 35% for far stars to 100% for near ones, so they start as dots and become long streaks rushing out from the center. Fade the stars in over 0.2s from 1.0s. Add a pale blue (120, 170, 255) glow: the white layer blurred with a Gaussian radius of 4, times 2.5, used as the blend amount toward the glow color, capped at 80%. From 3.1s to 3.4s, ease the whole frame to pure white; at 3.4s drop the void and stars, then ease the white back out to the untouched video by 3.8s. Before 0.8s and from 3.8s on the video must be unchanged. Don't use ffmpeg's drawtext filter. Keep the size, 30fps, length and audio, and save it as output.mp4.

Run it without Claude

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

python3 warp-speed.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Warp speed: the shot goes dark, white stars stretch into streaks rushing out from the center, then a flash lands back on the video.

Usage: python3 warp-speed.py input.mp4 output.mp4
Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import subprocess
import sys

import numpy as np
from PIL import Image, ImageDraw, ImageFilter

VOID = (2, 4, 18)            # deep blue-black the picture fades to
DARK = 0.9                   # how far toward VOID it fades (1 = the video is gone)
DARK_IN = (0.8, 1.1)         # seconds the picture fades to the void
STARS_FROM = 1.0             # second the stars appear, as still dots
STARS = 450                  # stars in the field
SPEED = (0.12, 2.6)          # star speed at the start and at full warp (share of the depth crossed per second)
RAMP = (1.4, 2.9)            # seconds the stars speed up between
TRAIL = 0.12                 # streak length, in seconds of travel: longer streaks the faster they go
GLOW = (120, 170, 255)       # pale blue glow around the streaks
FLASH = (3.1, 3.4, 3.8)      # white flash: starts, peaks (stars and void gone), fades back to the video
SEED = 5                     # change for a different star field


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 ramp(t, a, b):
    """0 before a, 1 after b, a smooth ease in between."""
    p = min(max((t - a) / (b - a), 0), 1)
    return p * p * (3 - 2 * p)


def speed(t):
    return SPEED[0] + (SPEED[1] - SPEED[0]) * ramp(t, *RAMP)


def project(x, y, z):
    """Screen position of a star at depth z (1 = far, near 0 = right at the viewer)."""
    return w / 2 + x / z, h / 2 + y / z


def starfield(t):
    """White streak layer (0-1) for time t: each star drawn from where it was TRAIL seconds ago to where it is now."""
    layer = Image.new("L", (w, h))
    draw = ImageDraw.Draw(layer)
    z_then = np.minimum(z + speed(t) * TRAIL, 1.0)
    x0, y0 = project(x, y, z_then)
    x1, y1 = project(x, y, z)
    near = 1 - z                                      # nearer stars are brighter and thicker
    fade = ramp(t, STARS_FROM, STARS_FROM + 0.2)
    for i in range(STARS):
        level = round(255 * fade * min(1, 0.35 + near[i] * 1.2))
        size = 1 + 2.5 * near[i]
        draw.line([(x0[i], y0[i]), (x1[i], y1[i])], fill=level, width=round(size))
        draw.ellipse([x1[i] - size / 2, y1[i] - size / 2, x1[i] + size / 2, y1[i] + size / 2], fill=level)
    return layer


def frame_at(img, t):
    flash = ramp(t, FLASH[0], FLASH[1]) if t < FLASH[1] else 1 - ramp(t, FLASH[1], FLASH[2])
    if t >= FLASH[1]:                                 # after the peak the video comes back clean
        out = img
    else:
        dark = DARK * ramp(t, *DARK_IN)
        out = img * (1 - dark) + np.array(VOID, np.float32) * dark
        if t >= STARS_FROM:
            core = starfield(t)
            glow = np.asarray(core.filter(ImageFilter.GaussianBlur(4)), np.float32)[..., None] / 255 * 2.5
            core = np.asarray(core, np.float32)[..., None] / 255
            out = out + (np.array(GLOW, np.float32) - out) * np.clip(glow, 0, 0.8)
            out = out + (255 - out) * core
    out = out + (255 - out) * flash
    return out.clip(0, 255).astype(np.uint8)


src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
rng = np.random.default_rng(SEED)
x = rng.uniform(-w * 0.6, w * 0.6, STARS)          # star positions, in pixels as seen at depth 1
y = rng.uniform(-h * 0.6, h * 0.6, STARS)
z = rng.uniform(0.15, 1.0, 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", "16", "-pix_fmt", "yuv420p", "-c:a", "copy", "-shortest", out],
                          stdin=subprocess.PIPE)
i = 0
while raw := reader.stdout.read(w * h * 3):
    t = i / fps
    img = np.frombuffer(raw, np.uint8).reshape(h, w, 3).astype(np.float32)
    writer.stdin.write(frame_at(img, t).tobytes())
    if t >= STARS_FROM:                               # move the stars toward the viewer; respawn the ones that pass
        z -= speed(t) / fps
        gone = z < 0.05
        x[gone] = rng.uniform(-w * 0.6, w * 0.6, gone.sum())
        y[gone] = rng.uniform(-h * 0.6, h * 0.6, gone.sum())
        z[gone] = 1.0
    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