---
title: "How to add embers and sparks with Claude (tested prompt and script)"
description: "Streaks of glowing sparks stream up from the bottom edge and swirl past the bottle, hot yellow as they enter and cooling to orange, then deep red, before they fade out. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/embers-and-sparks
---

# How to add embers and sparks with Claude

Streaks of glowing sparks stream up from the bottom edge and swirl past the bottle, hot yellow as they enter and cooling to orange, then deep red, before they fade out.

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 embers and sparks to input.mp4
   Second the first sparks enter at the bottom edge: 0.5
   New sparks per second: 36
   ```

## 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 embers and sparks 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                                                          |
| ------- | ----------------------------------------------- | --------------------------------------------------------------------- |
| `START` | `0.5`                                           | Second the first sparks enter at the bottom edge.                     |
| `RATE`  | `36`                                            | New sparks per second.                                                |
| `LIFE`  | `(2.8, 4.2)`                                    | Seconds a spark glows before it has cooled away.                      |
| `RISE`  | `(190, 340)`                                    | Upward speed of the slowest and fastest sparks, in pixels per second. |
| `SWIRL` | `(12, 40)`                                      | Sideways swing of each spark, in pixels.                              |
| `SIZE`  | `(1.6, 3.4)`                                    | Core radius of the smallest and largest sparks, in pixels.            |
| `TRAIL` | `0.07`                                          | Seconds of motion each spark smears into a streak.                    |
| `HEAT`  | `(255, 215, 90), (255, 140, 30), (200, 45, 10)` | Spark color at birth, halfway through its life and as it dies.        |

## Without the skill

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

```
Add rising embers and sparks to input.mp4 (540x960). From 0.5 seconds, 36 new sparks per second enter just below the bottom edge (10 px under it) at random positions between 5% and 95% across, until the end; before 0.5 seconds the frame is untouched. Each spark rises at a random 190 to 340 px/s, speeding up by 18 px/s every second (y = 970 - speed*age - 18*age^2), drifts sideways with a random wind of -25 to 25 px/s and swings side to side by 12 to 40 px on a 1.2 to 2.6 second cycle. It lives 2.8 to 4.2 seconds: its color goes from hot yellow (255, 215, 90) at birth to orange (255, 140, 30) halfway to deep red (200, 45, 10) at the end, it stays solid for the first half and then fades to nothing, flickering by 20%. Draw each spark as a Gaussian core with a radius of 1.6 to 3.4 px plus a halo four times as wide at about 20% strength, smeared into a streak along the last 0.07 seconds of its path and dimmer toward its tail. Paint the sparks over the footage with normal alpha (opaque cores, so they show on a light background), not a screen blend. Use Python with numpy and ffmpeg (don't use ffmpeg's drawtext filter for anything), keep the audio, and save it as output.mp4.
```

## Run it without Claude

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

```
python3 embers-and-sparks.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Embers and sparks: glowing sparks rise from the bottom edge and swirl up the frame, cooling from yellow to orange to red as they fade.

Usage: python3 embers-and-sparks.py input.mp4 output.mp4
Needs ffmpeg and numpy (pip install numpy).
"""
import json
import subprocess
import sys

import numpy as np

START = 0.5                  # second the first sparks enter at the bottom edge
RATE = 36                    # new sparks per second
LIFE = (2.8, 4.2)            # seconds a spark glows before it has cooled away
RISE = (190, 340)            # upward speed of the slowest and fastest sparks, in pixels per second
SWIRL = (12, 40)             # sideways swing of each spark, in pixels
SIZE = (1.6, 3.4)            # core radius of the smallest and largest sparks, in pixels
TRAIL = 0.07                 # seconds of motion each spark smears into a streak
STEPS = 12                   # dots that make up each streak
HEAT = [(255, 215, 90),      # color when the spark is born: hot yellow
        (255, 140, 30),      # halfway through its life: orange
        (200, 45, 10)]       # as it dies: deep red
SEED = 7                     # change for a different arrangement

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 position(s, age):
    """Where a spark is `age` seconds after it entered: rising, drifting with the wind and swinging side to side."""
    x = s["x"] + s["wind"] * age + s["swirl"] * np.sin(2 * np.pi * age / s["period"] + s["phase"])
    y = h + 10 - s["rise"] * age - 18 * age ** 2  # speeds up a little as the hot air carries it
    return x, y

def heat_color(p):
    """Color at life fraction p (0 = just born, 1 = cooled): yellow, then orange, then red."""
    a, b = (HEAT[0], HEAT[1]) if p < 0.5 else (HEAT[1], HEAT[2])
    f = p * 2 if p < 0.5 else p * 2 - 1
    return np.array(a) + (np.array(b) - np.array(a)) * f

def sparks(t):
    """The sparks at time t: their color and how much each pixel is covered (0-1)."""
    paint = np.zeros((h, w, 3), np.float32)
    cover = np.zeros((h, w), np.float32)
    for s in embers:
        age = t - s["born"]
        p = age / s["life"]
        if age < 0 or p >= 1:
            continue
        glow = min(1, 2 * (1 - p)) * (0.8 + 0.2 * np.sin(age * s["flicker"]))  # solid, then fades as it cools
        color = heat_color(p)
        r = s["size"]
        for k in range(STEPS):  # a short streak behind the spark, dimmer toward its tail
            x, y = position(s, max(age - TRAIL * k / STEPS, 0))
            weight = glow * (1 - k / STEPS) * 4 / STEPS
            rad = int(r * 6) + 1
            x0, y0, x1, y1 = max(int(x) - rad, 0), max(int(y) - rad, 0), min(int(x) + rad + 1, w), min(int(y) + rad + 1, h)
            if x0 >= x1 or y0 >= y1:
                continue
            yy, xx = np.mgrid[y0:y1, x0:x1].astype(np.float32)
            d2 = ((xx - x) ** 2 + (yy - y) ** 2) / r ** 2
            spot = weight * (1.6 * np.exp(-d2) + 0.35 * np.exp(-d2 / 16))  # a solid core in a halo four times as wide
            paint[y0:y1, x0:x1] += spot[..., None] * color
            cover[y0:y1, x0:x1] += spot
    color = paint / np.maximum(cover, 1e-6)[..., None]
    return color, np.minimum(cover, 1)[..., None]

src, out = sys.argv[1:3]
w, h, rate, fps, duration = probe(src)
rng = np.random.default_rng(SEED)
embers = []
for born in np.arange(START, duration, 1 / RATE):
    embers.append({"born": born + rng.uniform(0, 1 / RATE), "life": rng.uniform(*LIFE),
                   "x": rng.uniform(0.05, 0.95) * w, "wind": rng.uniform(-25, 25),
                   "rise": rng.uniform(*RISE), "size": rng.uniform(*SIZE),
                   "swirl": rng.uniform(*SWIRL), "period": rng.uniform(1.2, 2.6), "phase": rng.uniform(0, 2 * np.pi),
                   "flicker": rng.uniform(15, 30)})

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):
    img = np.frombuffer(raw, np.uint8).reshape(h, w, 3)
    if i / fps >= START:
        color, cover = sparks(i / fps)
        img = (img * (1 - cover) + color * cover).astype(np.uint8)
    writer.stdin.write(img.tobytes())
    i += 1
writer.stdin.close()
if reader.wait() or writer.wait():
    sys.exit("ffmpeg failed")
```

## More overlays and light

- [Anamorphic streak **Anamorphic streak**](https://www.recharm.com/effects/anamorphic-streak)
- [Aurora **Aurora**](https://www.recharm.com/effects/aurora)
- [Blood splatter **Blood splatter**](https://www.recharm.com/effects/blood-splatter)
- [Bokeh lights **Bokeh lights**](https://www.recharm.com/effects/bokeh-lights)
- [Camera flash **Camera flash**](https://www.recharm.com/effects/camera-flash)

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