---
title: "How to add an AI voice orb with Claude (tested prompt and script)"
description: "A glowing blue sphere under the bottle swells to nearly half again its size and wobbles at the rim on each spoken word, then shrinks back to rest when the voice stops. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/ai-voice-orb
---

# How to add an AI voice orb with Claude

A glowing blue sphere under the bottle swells to nearly half again its size and wobbles at the rim on each spoken word, then shrinks back to rest when the voice stops.

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 an AI voice orb to input.mp4
   Orb center, as shares of the frame width and height: 0.5, 0.83
   Resting orb radius as a share of the frame width: 0.13
   ```

## 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 ai voice orb 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                                                            |
| ----------------- | ------------------------------------------------- | ----------------------------------------------------------------------- |
| `X, Y`            | `0.5, 0.83`                                       | Orb center, as shares of the frame width and height.                    |
| `RADIUS`          | `0.13`                                            | Resting orb radius as a share of the frame width.                       |
| `SWELL`           | `0.45`                                            | How much bigger the orb gets at the loudest word.                       |
| `RIPPLE`          | `0.05`                                            | How far the rim wobbles at full loudness, as a share of the radius.     |
| `CORE, MID, EDGE` | `(255, 255, 255), (110, 200, 255), (30, 70, 230)` | The sphere's highlight, middle and rim colors.                          |
| `GLOW`            | `(70, 150, 255)`                                  | Halo color around the orb.                                              |
| `SCRIM`           | `0.75`                                            | How dark the shade behind the orb gets at the bottom edge (0 for none). |
| `RELEASE`         | `0.82`                                            | How slowly the orb shrinks back after a word (closer to 1 is slower).   |

## Without the skill

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

```
Add an AI voice orb to input.mp4, like an assistant's voice mode, driven by the clip's own voiceover. First darken the bottom of the frame with a black gradient that starts at 55% of the way down and reaches 75% opacity at the bottom edge (opacity rising as the 1.5 power of the distance). Then draw a sphere centered across and 83% of the way down, with a resting radius of 13% of the frame width. For each video frame, measure the audio's RMS loudness over that frame (1/30 s), divide by the loudest frame, raise to the power 0.6, and let it fall by at most 18% per frame (level = max(new, previous * 0.82)). The radius is the resting radius times (1 + 0.45 * level). Its rim wobbles by (0.15 + level) * 5% of the radius, with two waves turning in opposite directions: 0.6 * sin(3 * angle + 4t) + 0.4 * sin(5 * angle - 6t). Shade it like a lit sphere: measure each pixel's distance from a point 30% of the radius left and 35% above the center, divide by 1.35 times the rim radius, and color from white (255, 255, 255) at 0 through light blue (110, 200, 255) at 0.45 to deep blue (30, 70, 230) at 1. Around it put a (70, 150, 255) halo whose opacity is (0.5 + 0.5 * level) * exp(-distance past the rim / (17.5% of the radius)). Use Python with numpy for the loudness and Pillow to draw it, with ffmpeg to put it on the video (don't rely on 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 `ai-voice-orb.py` and run:

```
python3 ai-voice-orb.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""AI voice orb: a glowing white-to-blue sphere near the bottom swells, glows and ripples with the loudness of the voice.

Usage: python3 ai-voice-orb.py input.mp4 output.mp4  (the input needs a voice on its audio track)
Needs ffmpeg, numpy and Pillow (pip install numpy pillow).
"""
import json
import math
import subprocess
import sys

import numpy as np
from PIL import Image

X, Y = 0.5, 0.83            # orb center (0-1 across and down the frame)
RADIUS = 0.13               # resting orb radius as a share of the frame width
SWELL = 0.45                # how much the orb grows at the loudest word (0.45 = 45% bigger)
RIPPLE = 0.05               # how far the edge ripples at full loudness, as a share of the radius
CORE = (255, 255, 255)      # highlight color, up and to the left of the center
MID = (110, 200, 255)       # color halfway out
EDGE = (30, 70, 230)        # color at the rim
GLOW = (70, 150, 255)       # halo color around the orb
SCRIM = 0.75                # darkness of the shade behind the orb at the bottom edge (0 = none)
SCRIM_FROM = 0.55           # where the shade starts, as a share of the frame height
GLOW_SIZE = 0.35            # halo width as a share of the radius
RELEASE = 0.82              # how much of the swell carries over to the next frame as the voice dies away
RATE = 16000                # audio sample rate the loudness uses

def probe(path):
    info = json.loads(subprocess.check_output(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
                                               "stream=width,height,r_frame_rate:format=duration", "-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), float(info["format"]["duration"])

def loudness_per_frame(src, frames):
    """Voice loudness (0-1, 1 = loudest frame) for each video frame, rising at once and falling back gradually."""
    pcm = subprocess.check_output(["ffmpeg", "-v", "error", "-i", src, "-ac", "1", "-ar", str(RATE), "-f", "f32le", "-"])
    audio = np.frombuffer(pcm, np.float32)
    step = RATE / fps
    rms = np.array([np.sqrt(np.mean(audio[round(i * step):round((i + 1) * step)] ** 2) + 1e-12) for i in range(frames)])
    level = np.clip(rms / max(rms.max(), 1e-9), 0, 1) ** 0.6  # lift quiet syllables so every word shows
    for i in range(1, frames):
        level[i] = max(level[i], level[i - 1] * RELEASE)
    return level

def overlay(t, level):
    """The orb as an RGBA layer the size of the frame."""
    r0 = w * RADIUS * (1 + SWELL * level)
    reach = math.ceil(r0 * (1 + RIPPLE + GLOW_SIZE * 2))
    cx, cy = round(w * X), round(h * Y)
    ys, xs = np.mgrid[-reach:reach + 1, -reach:reach + 1].astype(np.float32)
    dist, angle = np.hypot(xs, ys), np.arctan2(ys, xs)
    # the rim wobbles with two waves turning opposite ways; a little idle wobble keeps it alive between words
    wobble = (0.15 + level) * RIPPLE * r0
    rim = r0 + wobble * (0.6 * np.sin(3 * angle + 4.0 * t) + 0.4 * np.sin(5 * angle - 6.0 * t))
    inside = np.clip(rim - dist + 0.5, 0, 1)  # one-pixel soft edge
    # shade it like a lit sphere: the highlight sits up and left, the far rim is the deepest blue
    depth = np.clip(np.hypot(xs + 0.3 * r0, ys + 0.35 * r0) / (1.35 * rim), 0, 1)
    color = np.dstack([np.interp(depth, [0, 0.45, 1], channel) for channel in zip(CORE, MID, EDGE)])
    halo = np.exp(-np.maximum(dist - rim, 0) / (GLOW_SIZE * r0 / 2)) * (0.5 + 0.5 * level) * (1 - inside)
    rgb = color * inside[..., None] + np.array(GLOW, np.float32) * halo[..., None]
    alpha = np.clip(inside + halo, 0, 1)
    rgb = rgb / np.maximum(alpha, 1e-6)[..., None]
    orb = Image.fromarray(np.dstack([rgb, alpha * 255]).clip(0, 255).astype(np.uint8))
    img = scrim.copy()
    img.alpha_composite(orb, (cx - reach, cy - reach))
    return img

def shade():
    """A black gradient that darkens the bottom of the frame so the orb glows against it."""
    rows = np.clip((np.arange(h) / h - SCRIM_FROM) / (1 - SCRIM_FROM), 0, 1) ** 1.5 * SCRIM * 255
    alpha = np.repeat(rows[:, None], w, axis=1).astype(np.uint8)
    img = Image.new("RGBA", (w, h))
    img.putalpha(Image.fromarray(alpha))
    return img

def render(src, out):
    """Draw the orb on every frame of src and save to out, keeping the audio."""
    frames = math.ceil(duration * fps)
    levels = loudness_per_frame(src, frames)
    ff = subprocess.Popen(["ffmpeg", "-y", "-v", "error", "-i", src,
                           "-f", "rawvideo", "-pix_fmt", "rgba", "-s", f"{w}x{h}", "-r", rate, "-i", "-",
                           "-filter_complex", "[0:v][1:v]overlay=shortest=1,format=yuv420p[v]",
                           "-map", "[v]", "-map", "0:a?", "-c:v", "libx264", "-crf", "18", "-c:a", "copy", out],
                          stdin=subprocess.PIPE)
    for i in range(frames):
        ff.stdin.write(overlay(i / fps, levels[i]).tobytes())
    ff.stdin.close()
    if ff.wait():
        sys.exit("ffmpeg failed")

src, out = sys.argv[1:3]
w, h, rate, fps, duration = probe(src)
scrim = shade()
render(src, out)
```

## More music-synced visuals

- [Audio-reactive blob **Audio-reactive blob**](https://www.recharm.com/effects/audio-reactive-blob)
- [Audio-reactive particles **Audio-reactive particles**](https://www.recharm.com/effects/audio-reactive-particles)
- [Audio-reactive text **Audio-reactive text**](https://www.recharm.com/effects/audio-reactive-text)
- [Audio waveform **Audio waveform**](https://www.recharm.com/effects/audio-waveform)
- [Audiogram **Audiogram**](https://www.recharm.com/effects/audiogram)

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