How to add a drunk vision with Claude

For three seconds the bottle tilts back and forth, splits into two see-through copies that drift apart and together, and slides in and out of focus before the shot snaps back to sharp.

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 drunk vision to input.mp4
    Second the effect starts: 1.0
    Seconds it lasts: 3.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 drunk vision 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
START1.0Second the effect starts.
LENGTH3.0Seconds it lasts.
EASE0.5Seconds it takes to build up at the start and fade out at the end.
TILT5Degrees the picture sways each way.
SWAY1.6Seconds for one full sway, left and back.
DOUBLE0.09How far apart the two copies drift at most, as a share of the frame width.
BLUR6Strongest blur radius, in pixels.
ZOOM1.24Scale-up at full strength, so the tilted picture never shows its edges. Raise it if you raise TILT or DOUBLE.

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 drunk vision effect to input.mp4 from 1.0 to 4.0 seconds; before 1.0 and after 4.0 seconds the video is unchanged. Strength k eases from 0 to 1 over the first 0.5 seconds and back to 0 over the last 0.5 seconds (smoothstep k = u²(3 − 2u), where u = min(t − 1, 4 − t, 0.5) / 0.5). With s = t − 1.0 in each frame: (1) tilt angle = 5° × k × sin(2π s / 1.6); (2) scale the picture up by 1 + 0.24 × k around the center so the tilted frame never shows an edge; (3) make two copies of the frame, each rotated and scaled around the center, one tilted by the full angle and moved left by g pixels, the other tilted by 0.8 × the angle and moved right by g pixels, where g = k × 0.09 × frame width × (0.6 + 0.4 × sin(2π s / 1.1)) / 2, and mix them 50/50 so you see double; (4) Gaussian-blur the mix with radius k × 6 × (0.55 + 0.45 × sin(2π s / 1.3)) pixels. Use Python with Pillow and ffmpeg, keep the size, frame rate and audio, and save it as output.mp4.

Run it without Claude

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

python3 drunk-vision.py input.mp4 output.mp4
Show the script
#!/usr/bin/env python3
"""Drunk vision: for three seconds the picture sways side to side, splits into two drifting copies and blurs in and out.

Usage: python3 drunk-vision.py input.mp4 output.mp4
Needs ffmpeg and Pillow (pip install pillow).
"""
import json
import math
import subprocess
import sys

from PIL import Image, ImageChops, ImageFilter

START = 1.0      # second the effect starts
LENGTH = 3.0     # seconds it lasts
EASE = 0.5       # seconds it takes to build up at the start and fade out at the end
TILT = 5         # degrees the picture sways each way
SWAY = 1.6       # seconds for one full sway, left and back
DOUBLE = 0.09    # how far apart the two copies drift at most, as a share of the frame width
BLUR = 6         # strongest blur radius, in pixels
ZOOM = 1.24      # scale-up at full strength, so the tilted picture never shows its edges


def strength(t):
    """0 outside the effect, easing up to 1 and back down over EASE seconds at each end."""
    u = min(t - START, START + LENGTH - t, EASE) / EASE
    u = max(u, 0)
    return u * u * (3 - 2 * u)


def tilted(img, angle, zoom, dx):
    """The frame turned by `angle` degrees and scaled by `zoom` around its center, then moved dx pixels across."""
    a = math.radians(angle)
    c, s = math.cos(a) / zoom, math.sin(a) / zoom
    cx, cy = w / 2, h / 2
    # each output pixel (x, y) reads from the input at (c*x + s*y + e, -s*x + c*y + f)
    e = cx - c * (cx + dx) - s * cy
    f = cy + s * (cx + dx) - c * cy
    return img.transform((w, h), Image.AFFINE, (c, s, e, -s, c, f), resample=Image.BICUBIC)


def drunk(img, t):
    k = strength(t)
    angle = k * TILT * math.sin(2 * math.pi * (t - START) / SWAY)
    zoom = 1 + (ZOOM - 1) * k
    gap = k * DOUBLE * w * (0.6 + 0.4 * math.sin(2 * math.pi * (t - START) / 1.1)) / 2
    left = tilted(img, angle, zoom, -gap)
    right = tilted(img, angle * 0.8, zoom, gap)
    double = ImageChops.blend(left, right, 0.5)
    radius = k * BLUR * (0.55 + 0.45 * math.sin(2 * math.pi * (t - START) / 1.3))
    return double.filter(ImageFilter.GaussianBlur(radius))


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)


src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
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):
    t = i / fps
    if START < t < START + LENGTH:
        raw = drunk(Image.frombytes("RGB", (w, h), raw), t).tobytes()
    writer.stdin.write(raw)
    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