---
title: "How to add a water ripple with Claude (tested prompt and script)"
description: "Two drops land, one at the bottle's neck and one on its label, and rings of ripples roll out from each, bending the bottle and backdrop like a reflection in a pond until the picture settles still. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/water-ripple
---

# How to add a water ripple with Claude

Two drops land, one at the bottle's neck and one on its label, and rings of ripples roll out from each, bending the bottle and backdrop like a reflection in a pond until the picture settles still.

Made by the script on this page, from our own test clip.

## Get this effect

1. ### Switch to Code in your [Claude Desktop App](https://claude.com/download)

2. ### Install the skill (once)

   ```
   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 water ripple to input.mp4
   When each drop lands and where, as (second, across, down) with positions from 0 to 1: (1.0, 0.50, 0.42), (2.0, 0.50, 0.62)
   Pixels per second the rings travel outward: 240
   ```

## 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 water ripple 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.

| Setting      | Default                                | What it does                                                                          |
| ------------ | -------------------------------------- | ------------------------------------------------------------------------------------- |
| `DROPS`      | `(1.0, 0.50, 0.42), (2.0, 0.50, 0.62)` | When each drop lands and where, as (second, across, down) with positions from 0 to 1. |
| `SPEED`      | `240`                                  | Pixels per second the rings travel outward.                                           |
| `WAVELENGTH` | `44`                                   | Distance between rings in pixels.                                                     |
| `TRAIN`      | `200`                                  | How deep the band of rings is behind the leading one, in pixels.                      |
| `AMP`        | `9`                                    | Furthest a ring bends the picture, in pixels.                                         |
| `SHINE`      | `22`                                   | Brightness of the light and dark bands on the rings.                                  |
| `LIFE`       | `2.8`                                  | Seconds a drop's rings take to die away.                                              |

## Without the skill

Don’t want to install the skill? Paste this prompt into Claude Code and Claude writes the script itself.

```
Add a water ripple effect to input.mp4, as if drops fell on a pond reflecting the shot. Two drops land: the first at 1.0 seconds at 50% across and 42% down, the second at 2.0 seconds at 50% across and 62% down. From each, a band of concentric rings travels outward at 240 pixels per second: rings 44 pixels apart, the band about 200 pixels deep behind the leading ring with soft edges, each ring pushing pixels in and out along the radius by up to 9 pixels (a sine wave), and adding light and dark bands of up to 22 brightness levels (a cosine of the same wave) so the rings catch the light. Each drop's rings swell in over 0.15 seconds and die away over 2.8 seconds, and the point of impact itself stays calm. Before 1.0 seconds and after 4.8 seconds the frame is untouched. Use Python with numpy and OpenCV (cv2.remap) to bend each frame and ffmpeg to decode and encode (this effect has no text; any text would be drawn with Pillow, not ffmpeg's drawtext filter). Keep the size, 30fps and the audio, and save it as output.mp4.
```

## Run it without Claude

This is the exact script we tested. Save it as `water-ripple.py` and run:

```
python3 water-ripple.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Water ripple: two drops hit the picture and rings of ripples roll outward from each, bending it like a reflection in a pond.

Usage: python3 water-ripple.py input.mp4 output.mp4
Needs ffmpeg, numpy and OpenCV to bend each frame (pip install numpy opencv-python-headless).
"""
import json
import subprocess
import sys

import cv2
import numpy as np

DROPS = [(1.0, 0.50, 0.42),   # (second, across, down) for each drop: when it lands and where (0-1 of the frame)
         (2.0, 0.50, 0.62)]
SPEED = 240        # pixels per second the rings travel outward
WAVELENGTH = 44    # distance between rings in pixels
TRAIN = 200        # how far the rings trail behind the leading one, in pixels
AMP = 9            # furthest a ring bends the picture, in pixels
SHINE = 22         # brightness of the light and dark bands on the rings
LIFE = 2.8         # seconds a drop's rings take to die away
FPS = 30

def probe(path):
    info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
                                               "stream=width,height:format=duration", "-of", "json", path]))
    return info["streams"][0]["width"], info["streams"][0]["height"], float(info["format"]["duration"])

def frames(path):
    """Decode a clip as RGB frames at FPS."""
    ff = subprocess.Popen(["ffmpeg", "-v", "error", "-i", path, "-vf", f"fps={FPS}",
                           "-f", "rawvideo", "-pix_fmt", "rgb24", "-"], stdout=subprocess.PIPE)
    while True:
        data = ff.stdout.read(w * h * 3)
        if len(data) < w * h * 3:
            return
        yield np.frombuffer(data, np.uint8).reshape(h, w, 3)

def ripples(t):
    """Sideways push (dx, dy) and light/shade for every pixel at time t, summed over the drops that have landed."""
    dx, dy, shade = np.zeros((h, w), np.float32), np.zeros((h, w), np.float32), np.zeros((h, w), np.float32)
    for start, x, y in DROPS:
        s = t - start
        if not 0 < s < LIFE:
            continue
        ox, oy = xs - x * w, ys - y * h
        r = np.hypot(ox, oy)
        d = SPEED * s - r                                              # how far behind the leading ring
        envelope = np.exp(-((d - TRAIN / 2) / (TRAIN / 2)) ** 4)       # a band of rings, soft at both ends
        fade = (1 - s / LIFE) ** 1.5 * min(s / 0.15, 1)                # swells in, then dies away
        k = envelope * fade * np.clip(r / 30, 0, 1)                    # calm right at the impact point
        phase = 2 * np.pi * d / WAVELENGTH
        push = AMP * k * np.sin(phase)
        dx += push * ox / np.maximum(r, 1)
        dy += push * oy / np.maximum(r, 1)
        shade += SHINE * k * np.cos(phase)
    return dx, dy, shade

src, out = sys.argv[1:3]
w, h, duration = probe(src)
ys, xs = np.mgrid[0:h, 0:w].astype(np.float32)

enc = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{w}x{h}",
                        "-r", str(FPS), "-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)
for i, frame in enumerate(frames(src)):
    dx, dy, shade = ripples(i / FPS)
    bent = cv2.remap(frame, xs - dx, ys - dy, cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT)
    enc.stdin.write(np.clip(bent + shade[..., None], 0, 255).astype(np.uint8).tobytes())
enc.stdin.close()
if enc.wait():
    sys.exit("ffmpeg failed")
```

## More distortion and glitch

- [Anaglyph 3D **Anaglyph 3D**](https://www.recharm.com/effects/anaglyph-3d)
- [Black hole **Black hole**](https://www.recharm.com/effects/black-hole)
- [Bulge and pinch **Bulge and pinch**](https://www.recharm.com/effects/bulge-pinch)
- [Datamosh **Datamosh**](https://www.recharm.com/effects/datamosh)
- [Disintegration **Disintegration**](https://www.recharm.com/effects/disintegration)

## 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](https://www.recharm.com/get-started)
