---
title: "How to add a floating product with Claude (tested prompt and script)"
description: "The serum bottle, lifted off its stand onto a plain lilac backdrop, drifts slowly up and down in midair while a soft shadow on the floor below darkens as it sinks and fades as it rises. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/floating-product
---

# How to add a floating product with Claude

The serum bottle, lifted off its stand onto a plain lilac backdrop, drifts slowly up and down in midair while a soft shadow on the floor below darkens as it sinks and fades as it rises.

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 floating product to input.mp4
   Where the product's base sits in the input, 0-1 down the frame; anything below it (the stand) is dropped: 0.775
   How far the product floats above its spot, as a share of the frame height: 0.09
   ```

## 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 floating product 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                                                                                              |
| ------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `BASE`              | `0.775`                            | Where the product's base sits in the input, 0-1 down the frame; anything below it (the stand) is dropped. |
| `LIFT`              | `0.09`                             | How far the product floats above its spot, as a share of the frame height.                                |
| `BOB`               | `0.025`                            | How far it bobs up and down from there, as a share of the frame height.                                   |
| `PERIOD`            | `2.0`                              | Seconds for one bob up and back down.                                                                     |
| `TOP, BOTTOM`       | `(242, 236, 252), (212, 200, 236)` | Backdrop gradient colors at the top and bottom of the frame.                                              |
| `SHADOW_Y`          | `0.79`                             | Shadow center, 0-1 down the frame.                                                                        |
| `SHADOW_W`          | `0.36`                             | Shadow width at the product's lowest point, as a share of the frame width.                                |
| `DARKEST, LIGHTEST` | `0.65, 0.35`                       | Shadow opacity at the product's lowest and highest points.                                                |

## Without the skill

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

```
Make the product in input.mp4 (540x960, 30fps, the camera circles a serum bottle standing on an acrylic block, its base 77.5% down the frame) float. Replace the whole background with a vertical gradient from RGB (242, 236, 252) at the top to (212, 200, 236) at the bottom. On every frame, cut the bottle out with rembg (u2net model) and drop the cut-out below 77.5% down (744 px) with a 4-pixel fade, so the block doesn't come with it. Move the cut-out straight up by 0.09 + 0.025 * sin(2 * pi * t / 2) of the frame height (rounded to whole pixels: 110 px at 0.5s, 62 px at 1.5s), so it bobs once every 2 seconds. Under it draw a soft shadow: an ellipse in RGB (70, 45, 100), centered horizontally at 79% down, 36% of the frame width across and 0.12 times as tall as it is wide when the bottle is lowest, shrinking to 80% of that width when it is highest, blurred with an 11.5-pixel Gaussian blur, at 65% opacity at the lowest point fading to 35% at the highest (blend linearly with the height). Layer the backdrop, then the shadow, then the moved bottle. Use Python with Pillow and numpy and ffmpeg to read and write the video, keep the size, 30fps, 6-second length and the audio, and save it as output.mp4.
```

## Run it without Claude

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

```
python3 floating-product.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Floating product: the cut-out product hovers over a clean lilac backdrop, bobbing gently above its soft shadow.

Usage: python3 floating-product.py input.mp4 output.mp4
Needs ffmpeg, Pillow, numpy and rembg to cut the product out of every frame
(pip install pillow numpy "rembg[cpu]"; rembg downloads its u2net model on first use).
"""
import json
import math
import subprocess
import sys

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

BASE = 0.775                 # where the product's base sits in the input, 0-1 down; anything below (its stand) is dropped
LIFT = 0.09                  # how far the product floats above its spot, as a share of the frame height
BOB = 0.025                  # bob up and down from there, as a share of the frame height
PERIOD = 2.0                 # seconds for one bob up and back down
TOP = (242, 236, 252)        # backdrop color at the top of the frame
BOTTOM = (212, 200, 236)     # backdrop color at the bottom
SHADOW_Y = 0.79              # shadow center, 0-1 down the frame (just under where the product stood)
SHADOW_W = 0.36              # shadow width at the product's lowest point, as a share of the frame width
SHADOW = (70, 45, 100)       # shadow color
DARKEST, LIGHTEST = 0.65, 0.35   # shadow opacity at the product's lowest and highest points

def backdrop():
    """A vertical gradient from TOP to BOTTOM."""
    k = np.linspace(0, 1, h, dtype=np.float32)[:, None, None]
    return np.broadcast_to(np.array(TOP, np.float32) * (1 - k) + np.array(BOTTOM, np.float32) * k, (h, w, 3))

def height_at(t):
    """How high the product floats at time t (0 = its lowest, 1 = its highest)."""
    return (1 + math.sin(2 * math.pi * t / PERIOD)) / 2

def shadow(up):
    """A soft ellipse that shrinks and fades as the product rises; returns its opacity per pixel."""
    sw = w * SHADOW_W * (1 - 0.2 * up)
    sh = sw * 0.12
    img = Image.new("L", (w, h))
    ImageDraw.Draw(img).ellipse([w / 2 - sw / 2, h * SHADOW_Y - sh / 2, w / 2 + sw / 2, h * SHADOW_Y + sh / 2], fill=255)
    img = img.filter(ImageFilter.GaussianBlur(h * 0.012))
    opacity = DARKEST + (LIGHTEST - DARKEST) * up
    return np.asarray(img, np.float32)[:, :, None] / 255 * opacity

def composite(rgb, t):
    """Backdrop, then the shadow, then the product cut out of the frame and moved up."""
    up = height_at(t)
    shift = round(h * (LIFT + BOB * (2 * up - 1)))
    cut = np.asarray(session.predict(Image.fromarray(rgb))[0], np.float32)[:, :, None] / 255 * above_base
    a = shadow(up)
    frame = base * (1 - a) + np.array(SHADOW, np.float32) * a
    product, alpha = np.zeros_like(frame), np.zeros_like(cut)
    product[:h - shift], alpha[:h - shift] = rgb[shift:], cut[shift:]
    frame = frame * (1 - alpha) + product * alpha
    return frame.round().astype(np.uint8)

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 render(src, out):
    """Read src frame by frame, composite each one and save to out, keeping the audio."""
    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):
        rgb = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
        writer.stdin.write(composite(rgb, i / fps).tobytes())
        i += 1
    writer.stdin.close()
    if reader.wait() or writer.wait():
        sys.exit("ffmpeg failed")

src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
base = backdrop()
above_base = np.clip((h * BASE - np.arange(h, dtype=np.float32)) / 4, 0, 1)[:, None, None]
session = new_session("u2net")
render(src, out)
```

## More product shots

- [Animated feature callouts **Animated feature callouts**](https://www.recharm.com/effects/animated-feature-callouts)
- [Before/after slider **Before/after slider**](https://www.recharm.com/effects/before-after-slider)
- [Best seller badge **Best seller badge**](https://www.recharm.com/effects/best-seller-badge)
- [Callout arrows and circles **Callout arrows and circles**](https://www.recharm.com/effects/callout-arrows-and-circles)
- [Carousel swipe **Carousel swipe**](https://www.recharm.com/effects/carousel-swipe)

## 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)
