---
title: "How to add a fire with Claude (tested prompt and script)"
description: "A ragged row of flames shoots up from the bottom edge and keeps licking under the bottle, pale yellow at the base, orange in the middle and red at the flickering tips, the tallest reaching about a third of the way up the frame. Install one skill and ask Claude Code, or run the Python script we tested yourself."
url: https://www.recharm.com/effects/fire
---

# How to add a fire with Claude

A ragged row of flames shoots up from the bottom edge and keeps licking under the bottle, pale yellow at the base, orange in the middle and red at the flickering tips, the tallest reaching about a third of the way up the frame.

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 fire to input.mp4
   Palette: red (205, 35, 0) at the tips to pale yellow (255, 238, 165) at the base
   ```

## 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 fire 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 bottom edge catches fire.                                               |
| `GROW`    | `0.8`                                                                     | Seconds the flames take to climb to full height.                                   |
| `HEIGHT`  | `0.36`                                                                    | How high the tallest flame tips reach, as a share of the frame height.             |
| `RISE`    | `1.2`                                                                     | How fast the fire churns upward, in flame heights per second.                      |
| `FLICKER` | `2.5`                                                                     | How often each tongue of flame leaps and drops, per second.                        |
| `CELL`    | `4`                                                                       | Size in pixels of the grid squares the fire is worked out on before it's smoothed. |
| `PALETTE` | `red (205, 35, 0) at the tips to pale yellow (255, 238, 165) at the base` | Fire color at each heat, from the cool tips to the hot base.                       |

## Without the skill

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

```
Add a bank of flames along the bottom of input.mp4 (540x960, 30 fps). Before 0.5 seconds the frame is untouched; at 0.5 seconds the bottom edge catches fire and the flames climb to full height over 0.8 seconds (easing out), then keep burning to the end. The fire lives in the bottom 36% of the frame (the bottom 346 px) and never goes above it; the top 64% stays exactly as it was, with no color grade. Use Python with numpy: work the fire out on a coarse grid of 4x4 px cells (135 columns x 86 rows) and smooth it up to full size with linear interpolation. Make two smooth noise textures 4x the band's height that wrap round (Gaussian white noise, filtered in the FFT by 1/f^1.6 with a Gaussian roll-off at 0.35 cycles per cell, the vertical frequency multiplied by 3 for one and 2 for the other so the blobs are tall and thin), scroll them up through the band at 1.2 and 2 band heights per second and mix them 60/40. Give each column its own flame height between 15% and 100% of the band from a third smooth noise strip that changes 2.5 times a second, so separate tongues leap and drop. Heat = clip(1.8 x noise + 0.5 - 2 x height above the bottom edge / that column's flame height, 0, 1). Color the heat from transparent at 0 through red (205, 35, 0) at 0.2, orange (250, 100, 5) at 0.45 and yellow-orange (255, 170, 30) at 0.7 to pale yellow (255, 238, 165) at 1, with opacity = 2.5 x heat (capped at 1), and paint it over the footage with normal alpha, not a screen blend, so it shows on a light background. 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 `fire.py` and run:

```
python3 fire.py input.mp4 output.mp4
```

```
#!/usr/bin/env python3
"""Fire: flames shoot up from the bottom edge and keep licking and flickering along the bottom of the frame.

Usage: python3 fire.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 bottom edge catches fire
GROW = 0.8                    # seconds the flames take to climb to full height
HEIGHT = 0.36                 # how high the tallest flame tips reach, as a share of the frame height
RISE = 1.2                    # how fast the fire churns upward, in flame heights per second
FLICKER = 2.5                 # how often each tongue of flame leaps and drops, per second
CELL = 4                      # the fire is worked out on a grid of CELL x CELL pixel squares, then smoothed
PALETTE = [(0.00, (0, 0, 0)),          # heat 0: nothing
           (0.20, (205, 35, 0)),       # red at the tips
           (0.45, (250, 100, 5)),      # orange
           (0.70, (255, 170, 30)),     # yellow-orange
           (1.00, (255, 238, 165))]    # pale yellow at the base
SEED = 3                      # change for a different flame pattern

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)

def turbulence(n_rows, stretch):
    """A smooth random pattern (0-1) that wraps round at every edge, its blobs `stretch` times taller than wide."""
    fy = np.fft.fftfreq(n_rows)[:, None] * stretch
    fx = np.fft.rfftfreq(cols)[None, :]
    f = np.hypot(fx, fy)
    f[0, 0] = 1
    spectrum = np.fft.rfft2(rng.standard_normal((n_rows, cols))) / f ** 1.6 * np.exp(-(f / 0.35) ** 2) * (f > 0.01)
    tex = np.fft.irfft2(spectrum, (n_rows, cols))
    lo, hi = np.percentile(tex, [1, 99])
    return np.clip((tex - lo) / (hi - lo), 0, 1)

def row_at(tex, pos):
    """Row `pos` of tex (fractional, wrapping round), or rows if pos is an array."""
    pos = np.asarray(pos) % len(tex)
    i0 = pos.astype(int)
    frac = (pos - i0)[..., None]
    return tex[i0] * (1 - frac) + tex[(i0 + 1) % len(tex)] * frac

def stretch_matrix(n, size):
    """Matrix that resizes n samples to `size` with linear interpolation."""
    pos = np.clip((np.arange(size) + 0.5) / size * n - 0.5, 0, n - 1)
    m = np.zeros((size, n), np.float32)
    i0 = np.minimum(pos.astype(int), n - 2)
    m[np.arange(size), i0] = 1 - (pos - i0)
    m[np.arange(size), i0 + 1] = pos - i0
    return m

def flames(t):
    """Fire color and opacity (0-1) for the bottom rows*CELL pixels of the frame at time t."""
    reach = max(min((t - START) / GROW, 1), 0.01) ** 0.7                 # climbs fast, then settles at full height
    rise = RISE * rows * (t - START)                                     # rows the texture has slid up through the band
    churn = 0.6 * row_at(big, np.arange(rows) + rise) + 0.4 * row_at(small, np.arange(rows) + 1.7 * rise)
    tongue = 0.15 + 0.85 * row_at(tongues, t * FLICKER * 8)              # each column's flame height right now
    up = np.linspace(1, 0, rows)[:, None] / (reach * tongue)             # 0 at the bottom edge, 1 at that column's tip
    heat = np.clip(1.8 * churn + 0.5 - 2.0 * up, 0, 1)
    color = [up_rows @ np.interp(heat, stops, channel) @ up_cols for channel in palette]
    alpha = up_rows @ np.clip(heat * 2.5, 0, 1) @ up_cols
    return np.stack(color, axis=-1), alpha[..., None]

src, out = sys.argv[1:3]
w, h, rate, fps = probe(src)
rng = np.random.default_rng(SEED)
rows, cols = round(h * HEIGHT / CELL), -(-w // CELL)
big, small = turbulence(4 * rows, 3.0), turbulence(4 * rows, 2.0)
tongues = turbulence(256, 0.5)  # one row per eighth of a flicker
stops = [s for s, _ in PALETTE]
palette = np.array([c for _, c in PALETTE], np.float32).T
up_rows, up_cols = stretch_matrix(rows, rows * CELL), stretch_matrix(cols, w).T

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).copy()
    if i / fps >= START:
        color, alpha = flames(i / fps)
        band = img[h - len(color):]
        band[:] = band * (1 - alpha) + color * alpha
    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)
