How to simulate cassette tape audio with FFmpeg (2026)
Simulate cassette tape audio with FFmpeg: layer tape hiss, wow and flutter, bandwidth roll-off, and analog EQ in one filter_complex chain. Copy-paste commands.
The cassette sound is not one effect — it is four small imperfections stacked on top of a clean recording, and FFmpeg can bake all four into a track with a single filter chain, no DAW or plugin required. What your ear reads as "cassette" is tape hiss (the constant background rush), wow and flutter (a slow pitch drift plus a faster warble from an imperfect tape transport), a narrowed frequency range (analog tape rolls off the highest highs and the lowest lows), and a soft midrange coloration from the way tape saturates. Get those four right and a pristine WAV sounds like it was dubbed onto a C90 in 1985.
This matters for creators because the lofi and retro aesthetic is a genuine content category — lofi study mixes, vaporwave edits, nostalgic podcast intros, retro brand stings, and the warm audio bed under a slow B-roll reel. Paying for a tape-emulation plugin is one route; FFmpeg is the free, scriptable one that runs the same treatment across a whole folder of files without opening an app. This guide builds the chain one imperfection at a time so you understand each knob, then shows how to dial it toward a specific tape — a clean premium cassette versus a hissy budget one are the same filters with different numbers. Every command here is verified against a working open-source cassette-simulation project; the filters are all standard FFmpeg, so nothing extra to install beyond FFmpeg itself.
The steps
Know the four ingredients you are recreating. Before touching a command, fix what "cassette" actually means so you can tune it. (1) Hiss — a steady low-level noise floor from the tape's magnetic grain. (2) Wow and flutter — pitch instability: wow is the slow drift (roughly 0.5–4 Hz) from an eccentric capstan or spool, flutter is the faster warble (above ~4 Hz) from friction and tape guides. (3) Limited bandwidth — analog cassette rolls the treble off well before 20 kHz (a good tape reaches ~14–15 kHz, a cheap one ~9–11 kHz) and drops subsonic rumble at the bottom. (4) Coloration — tape gently bumps the low-mids and rounds the top, giving that "warm" tone. Each maps to one FFmpeg filter, so you build the sound by stacking them.
Start with a clean, normalized base signal. Open the chain by resampling to a cassette-era rate and forcing a known format so every later filter behaves. Use `aresample=44100,aformat=sample_fmts=fltp:channel_layouts=stereo` as the first stage and label it `[music]`. 44.1 kHz is the standard target and floating-point planar (`fltp`) keeps headroom for the noise-mix and saturation stages that follow. Doing this up front means the same recipe works whether the source is a mono voice memo or a stereo master.
Add tape hiss with a noise source and amix. Generate a quiet stereo noise bed and blend it under the music. `aevalsrc=exprs='(random(0)-0.5)*0.008|(random(1)-0.5)*0.008':s=44100[hiss]` makes independent left/right white noise at low amplitude, then `[music][hiss]amix=inputs=2:duration=first:normalize=0[mixed]` layers it in. Two details are load-bearing: `duration=first` stops the render at the end of the music (the noise source is infinite and would otherwise run forever), and `normalize=0` keeps the music at full volume instead of amix halving both inputs. Raise the `0.008` multiplier for a louder, cheaper-sounding tape; lower it for a premium one.
Add wow and flutter with two vibrato passes. FFmpeg's `vibrato` filter modulates pitch, which is exactly what a wobbling tape transport does. Chain two of them: a slow one for wow and a fast one for flutter — `vibrato=f=2:d=0.025,vibrato=f=14:d=0.007`. Here `f` is the wobble rate in Hz and `d` is depth (0–1). The ~2 Hz pass is the slow drift; the ~14 Hz pass is the faster warble. Keep depths small — 0.02–0.03 for wow and under 0.01 for flutter — because real cassette pitch error is subtle; crank it and the track sounds seasick rather than vintage.
Narrow the bandwidth with highpass and lowpass. Cut the frequencies tape could not hold. `highpass=f=50` removes subsonic rumble at the bottom; `lowpass=f=13500` rolls off the sparkle at the top the way ferric tape does. The lowpass cutoff is your single biggest "which tape" control: ~14000–15000 for a clean premium cassette, ~9000–11000 for a muffled budget one. Add these right after the vibrato stages so the pitch wobble is inside the band you keep.
Color the tone with a couple of EQ bands. Finish the character with `equalizer` bands that mimic tape's frequency response. A gentle low-mid bump plus a slight upper-treble cut reads as "warm": `equalizer=f=250:width_type=q:width=1:g=0.5,equalizer=f=8000:width_type=q:width=0.7:g=-3.5`. Here `f` is the center frequency, `g` is gain in dB (positive boosts, negative cuts), and `width_type=q:width=…` sets how wide the band is. Keep moves modest — a couple of dB. This is the step that separates a convincing tape from a generically muffled one.
Optional: saturation and dropouts for extra realism. For a harder-worn sound, add `asoftclip=type=tanh` before the EQ to round transient peaks the way tape compression does (tanh is the smoothest of the soft-clip curves). For an old, damaged tape, occasional level dips or brief mutes sell it — you can automate a slow amplitude wobble with `tremolo=f=0.2:d=0.15`. Use these sparingly; a little saturation warms the track, a lot turns it to mush. Skip this step entirely for a clean, well-maintained-tape feel.
Assemble the full chain and batch-export. Put it together in one `-filter_complex`, map the final `[out]` label, and encode. A complete Sony-CHF-style command: `ffmpeg -i input.wav -filter_complex "aresample=44100,aformat=sample_fmts=fltp:channel_layouts=stereo[music]; aevalsrc=exprs='(random(0)-0.5)*0.008|(random(1)-0.5)*0.008':s=44100[hiss]; [music][hiss]amix=inputs=2:duration=first:normalize=0[mixed]; [mixed]vibrato=f=2:d=0.025,vibrato=f=14:d=0.007,highpass=f=50,lowpass=f=13500,equalizer=f=250:width_type=q:width=1:g=0.5,equalizer=f=8000:width_type=q:width=0.7:g=-3.5[out]" -map "[out]" -c:a libmp3lame -b:a 192k output.mp3`. To process a whole folder, drop it inside a `for file in *.wav; do … done` loop and change the output name per file — the free tape-simulation projects that inspired this are exactly that: one filter chain in a shell loop.
Common gotchas
The noise source is infinite. If you forget `duration=first` on the amix (or `-shortest` on the output), FFmpeg keeps generating hiss forever and the render never ends. Always cap the length to the music input.
amix quietly halves your volume by default. Its `normalize` option defaults to 1, which scales every input down by the number of inputs — set `normalize=0` so the music stays at full level and the hiss just sits under it.
Vibrato depth is the seasick knob. Depths above ~0.05 turn subtle tape wobble into an obvious pitch warble. Real wow/flutter is small; start low (0.02–0.03 for wow, under 0.01 for flutter) and only nudge up.
The lowpass cutoff, not the noise, is what makes it "sound like a cheap tape." Muffled comes from rolling the treble off early (9–11 kHz), not from piling on hiss. Tune the lowpass first, then the noise level.
Order matters in the chain. Put the pitch (vibrato) and band-limiting (highpass/lowpass) after the noise is mixed in, so the whole blended signal gets the tape treatment — not just the music.
Re-encoding to a lossy codec adds its own artifacts on top. That is usually fine (period-accurate, even) for a cassette vibe, but if you are stacking further processing later, export to WAV first and compress once at the end.
This is coloration, not restoration. The effect is deliberately destructive — it discards high frequencies and adds noise. Keep your clean master; you cannot un-cassette a file after the fact.
Where Kompozy fits
FFmpeg colors the audio; it does not build the audience that the lofi/retro aesthetic exists to reach. That is the split worth being precise about — Kompozy does not apply cassette effects to your files, and it is not a repurposing tool bolted onto your workflow. It is a content generation and multi-platform publishing engine, and the tape-processed track is the raw material it turns into a distributed release. Concretely: once you have a cassette-treated mix or audio bed from the chain above, Kompozy generates the visual content that carries it — a Listicle Video or Naturalistic Video that lays your warm audio under portrait footage for a Reel or Short, Clipped Shorts that slice a long lofi mix into platform-native teasers, and Persona Shorts where a consistent avatar narrates the "how I made this cassette sound" story over the track. Around that it drafts the words as net-new content, not reheated captions: a Blog Article that is this exact tutorial for SEO, a Carousel walking the four-imperfection recipe, Quote Graphics of the sharp lines, and an Email Newsletter to your list — all on managed Claude and OpenAI models inside one flat subscription. Then autopilot fans every piece across eight social platforms plus blog and email on a schedule, with a per-post review gate so nothing ships off-brand. The division of labor is clean: FFmpeg makes it sound like 1985, and Kompozy makes it a release that shows up everywhere your lofi audience already is. Starter ($99/mo, 5,500 credits) fits a solo producer shipping one aesthetic and one channel; Pro ($299/mo, 18,000 credits) runs a heavier multi-platform cadence with autopilot when you are releasing regularly; Enterprise is custom for a label or audio brand marketing many drops at once.
Frequently asked questions
Do I need any plugins to make cassette audio in FFmpeg?
No. Every filter used — aevalsrc, amix, vibrato, highpass, lowpass, equalizer, and optionally asoftclip and tremolo — ships with a standard FFmpeg build. You only need FFmpeg installed; there is no plugin, VST, or DAW involved, which is the whole appeal of the command-line route.
What is the difference between wow and flutter?
Both are pitch instability from an imperfect tape transport, separated by speed. Wow is the slow drift, roughly 0.5–4 Hz, from an eccentric capstan or spool — a gentle rise and fall in pitch. Flutter is the faster warble above about 4 Hz from friction and tape guides. In FFmpeg you model them as two vibrato passes: a low-frequency one for wow and a higher-frequency one for flutter.
How do I make it sound like a specific cassette?
Change the numbers, not the filters. A premium tape is quiet and bright — lower the noise multiplier (around 0.006), push the lowpass to 14000–15000, and use tiny vibrato depths. A cheap tape is noisier and duller — raise the noise (0.02+), drop the lowpass to 9000–11000, and increase the wow/flutter depth. The chain stays identical; the values are the tape.
Can I run this on a whole folder of files at once?
Yes, and that is the reason to use FFmpeg over a plugin. Wrap the single command in a shell loop (`for file in *.wav; do ffmpeg -i "$file" -filter_complex "…" -map "[out]" out/"$file".mp3; done`) and it applies the exact same tape treatment to every file unattended — no clicking through a DAW once per track.
Does the cassette effect hurt audio quality?
On purpose, yes — it is a lo-fi effect, so it intentionally removes high frequencies and adds hiss and pitch wobble. That is the point for a retro or lofi aesthetic. Just keep it as a coloration pass on a copy: always retain the clean master, because the processing is destructive and cannot be reversed once encoded.