Uploading a podcast episode to YouTube, posting a music sample to social media, putting a waveform thumbnail in an article — whenever an audio-only asset needs a picture, FFmpeg can draw waveforms and spectrograms without any external tool. There are three families of filters, chosen by what you want.
| You want | Filter | Output |
|---|---|---|
| A static waveform of the whole file | showwavespic |
one PNG |
| A waveform that moves with playback | showwaves |
video |
| Frequency content, still / moving | showspectrumpic / showspectrum |
PNG / video |
| Frequency bars (equaliser look) | showfreqs |
video |
Verified: FFmpeg 8.1 on Windows — all 12 commands in this article were executed by the verification script and passed (2026-09-06).
Static waveform (showwavespic)
ffmpeg -i input.mp3 -filter_complex "showwavespic=s=1920x480" -frames:v 1 waveform.png
The whole track is compressed into one image. -frames:v 1 means “output a single frame” — without it FFmpeg keeps writing the same picture forever. The background is transparent (RGBA).
Colour and size
ffmpeg -i input.mp3 -filter_complex "showwavespic=s=1920x480:colors=#00d4aa" -frames:v 1 waveform.png
colors takes hex or a colour name (white, red, …). For a stereo file with the channels drawn one above the other in different colours, use split_channels=1 and |-separated colours.
ffmpeg -i input.mp3 -filter_complex "showwavespic=s=1920x480:split_channels=1:colors=#00d4aa|#ff6b6b" -frames:v 1 waveform.png
Adding a background colour
When transparency is a problem (social-media images, for instance), paint a background with the color source and overlay the waveform on it.
ffmpeg -i input.mp3 -filter_complex "color=c=#0f1117:s=1920x480[bg];[0:a]showwavespic=s=1920x480:colors=#00d4aa[fg];[bg][fg]overlay=format=auto" -frames:v 1 waveform.png
Appearance options
| Option | Values | Effect |
|---|---|---|
scale |
lin (default) / log / sqrt / cbrt |
Amplitude scale. Quiet material like speech is easier to see with sqrt or log |
draw |
scale (default) / full |
full draws every sample as a line, so the picture is denser and looks almost filled |
filter |
average (default) / peak |
When several samples share a pixel, take the average or the peak |
ffmpeg -i input.mp3 -filter_complex "showwavespic=s=1920x480:draw=full" -frames:v 1 waveform.png
Animated waveform video (showwaves)
ffmpeg -i input.mp3 -filter_complex "[0:a]showwaves=s=1280x720:mode=cline:rate=30:colors=#00d4aa,format=yuv420p[v]" -map "[v]" -map 0:a -c:v libx264 -c:a aac -shortest output.mp4
s=1280x720— output resolutionmode=cline— filled symmetrically around the centre line. Alsopoint,line, andp2p(points joined by lines)rate=30— video frame rate (default 25)format=yuv420p— showwaves outputs RGBA; convert to yuv420p so the H.264 plays everywhere-shortest— end the video when the audio ends
-filter_complex rather than -vf is required because this filter takes an audio stream in and produces a video stream — it crosses media types.
With a background colour
showwaves also has a transparent background, so lay one down the same way as for the still image. Give the background the same frame rate with r=30.
ffmpeg -i input.mp3 -filter_complex "color=c=#0f1117:s=1280x720:r=30[bg];[0:a]showwaves=s=1280x720:mode=cline:rate=30:colors=#00d4aa[w];[bg][w]overlay=format=auto[v]" -map "[v]" -map 0:a -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest output.mp4
Waveform over cover art (podcast layout)
Turn a still image into video with -loop 1 and place a thin waveform strip along the bottom. This is the most common layout for publishing podcasts and tracks.
ffmpeg -loop 1 -i cover.png -i input.mp3 -filter_complex "[0:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2[bg];[1:a]showwaves=s=1280x200:mode=cline:rate=30:colors=#00d4aa[w];[bg][w]overlay=0:520:format=auto[v]" -map "[v]" -map 1:a -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest output.mp4
scale=...:force_original_aspect_ratio=decrease,pad=... is the standard idiom for fitting an image inside 1280×720 and filling the margins with black. overlay=0:520 puts the 200-pixel-tall waveform at the bottom of the frame (y = 520–720).
Spectrogram (showspectrumpic / showspectrum)
Time on the x-axis, frequency on the y-axis, intensity as colour. Noise bands and the presence or absence of high frequencies (the 16 kHz cut-off in an MP3, for example) are obvious at a glance, which makes it useful for quality checks too.
ffmpeg -i input.mp3 -filter_complex "showspectrumpic=s=1920x480" -frames:v 1 spectrum.png
By default a legend with axes and ticks is drawn. Turn it off with legend=0 for design use. Colour schemes include color=intensity (default channel).
ffmpeg -i input.mp3 -filter_complex "showspectrumpic=s=1920x480:legend=0:color=intensity" -frames:v 1 spectrum.png
The moving version is showspectrum; slide=scroll makes it flow from right to left.
ffmpeg -i input.mp3 -filter_complex "[0:a]showspectrum=s=1280x720:mode=combined:color=intensity:slide=scroll[v]" -map "[v]" -map 0:a -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest output.mp4
Frequency bars (showfreqs)
An equaliser-style visualiser with one bar per frequency band.
ffmpeg -i input.mp3 -filter_complex "[0:a]showfreqs=s=1280x720:mode=bar:colors=#00d4aa[v]" -map "[v]" -map 0:a -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest output.mp4
showfreqs also has a rate option, defaulting to 25 fps (we confirmed the output is 25/1). Add rate=30 for 30 fps.
Measured: render time and file size
We ran each filter on a 3-minute (180 s) stereo MP3 and recorded processing time and output size. The source is pink noise generated with anoisesrc, which is the worst case for waveform complexity — real music and speech produce smaller files. Encoded with libx264 -preset veryfast -crf 23 on an i9-14900KF, FFmpeg 8.1, measured 2026-09-02.
| Output | Time | Size |
|---|---|---|
showwavespic 1920×480 PNG |
0.24 s | 8 KB |
showspectrumpic 1920×480 PNG |
0.46 s | 1.2 MB |
showwaves cline 1280×720 / 30 fps / 3 min |
15.5 s | 82 MB |
showwaves cline 1920×1080 / 30 fps / 3 min |
27.4 s | 86 MB |
showspectrum 1280×720 / 3 min |
6.5 s | 9.5 MB |
showfreqs bar 1280×720 / 3 min |
13.2 s | 32 MB |
Two things stand out.
showwavesand H.264 don’t get along. Thin lines jump around the whole frame every frame, motion prediction barely works, and 3 minutes came out at 82 MB (about 3.6 Mbps). The spectrogram video of the same audio is 9.5 MB. To keep a waveform video small, in order of effect: usemode=clinefor filled areas, confine the waveform to part of the frame (a 200-pixel strip), and raise-crf.- 720p and 1080p are almost the same size (82 MB vs 86 MB). The number of lines doesn’t change, so a higher resolution only costs render time.
Pink noise is an extreme input; a spoken-word podcast lands at a fraction of these sizes. The pattern holds, though.
FAQ
The output is all black / no waveform visible
showwaves and showwavespic output a transparent (RGBA) background, which players and viewers that can’t handle transparency show as black. Add a background with color + overlay as shown above. Quiet recordings become visible with scale=sqrt or scale=log.
I want one waveform for a stereo file
showwavespic draws the channels on top of each other by default (split_channels=0). For a truly single trace, downmix first with aformat=channel_layouts=mono in front of it.
The waveform video has no sound
You forgot -map 0:a. As soon as you write any -map, FFmpeg’s automatic stream selection is disabled, so once you’ve selected the video with -map "[v]" you have to map the audio explicitly too.
Related tool
The Waveform video tool generates a showwaves-style video from an audio file in your browser.
Related articles
- Extract audio — pull just the audio out of a video
- Measure volume (volumedetect) — check levels before drawing
- Loudness normalisation — even out levels before publishing
- Create GIFs — for a short animated waveform GIF