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 resolution
  • mode=cline — filled symmetrically around the centre line. Also point, line, and p2p (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.

  1. showwaves and 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: use mode=cline for filled areas, confine the waveform to part of the frame (a 200-pixel strip), and raise -crf.
  2. 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.

The Waveform video tool generates a showwaves-style video from an audio file in your browser.