Making a simple image slideshow with moviepy
Posted on Wed 01 April 2026 in Software
I am working on a project in which I have a sequence of photos (JPEG files, all the same size) that I want to put into a slideshow, along with a background soundtrack.
The examples that Stack Overflow, Reddit, Google AI and Claude gave me were all incorrect: imports didn't work. Presumably the moviepy API appears to have changed significantly recently, with breaking changes to the package structure.
I eventually worked something out. The following is for moviepy==2.2.1:
from moviepy import *
import numpy as np
import pandas as pd
from streamerate import stream
# Load the labels file (see below)
df = pd.read_csv(
labels_file,
sep=r'\s',
header=None,
names=['timestamp', '_time_end', 'label']
)
timestamps = df['timestamp']
labels = df['label']
df['filename'] = (
stream(df['label'])
.map(lambda s: s + ".jpg")
).to_list()
ac = AudioFileClip(audio_file)
# Duration of each image
# Last image needs to go until the entire audio file finishes
durations = np.diff(
pd.concat([timestamps, pd.Series([a.duration])])
)
df['duration'] = durations
# Prepare image clips
imclips = []
for r in df.itertuples():
ic = ImageClip(r.filename, duration=r.duration)
# Fade in and out. Note that this is NOT a crossfade.
# Instead it fades to black each time and then to the
# new image.
ic = fx.FadeIn(1).apply(ic)
ic = fx.FadeOut(1).apply(ic)
imclips.append(ic)
video = concatenate_videoclips(imclips)
video.audio = ac
# For testing purposes, just write a few seconds, don't write the whole thing
video = video.subclipped(0, 30)
# Save the file
video.write_videofile('test.mp4', fps=10)
The labels_file specifies when each image should be placed. It is a tab-separated values file that looks something like this:
0.000000 0.000000 Image1
1.173660 1.173660 Image2
2.967547 2.967547 Image3
7.118296 7.118296 Image4
50.742082 50.742082 Image5
This is the label format that Audacity exports. All my photos have extension .jpg so it isn't necessary to have that in the labels.