How Spotify crossfades between songs without a gap
How Spotify pre-buffers the next track's first few seconds, applies gain normalization, computes the overlap window, and blends audio PCM frames for gapless playback.
The Problem Statement
Interviewer: "You are listening to a Spotify playlist. The song ends and the next one begins playing immediately, overlapping slightly, fading out the old one and fading in the new one. How does that actually work? What has to happen before the current song ends, and how does Spotify blend two tracks together?"
This question tests whether you understand client-side audio pipeline engineering. It is not a distributed systems question. The interesting work happens entirely on the device you are holding.
Most candidates say "Spotify pre-loads the next song" and stop there. The strong answer explains what "pre-loading" actually means (at what point, how much, from what buffer), describes the PCM-level crossfade computation, and covers why gain normalization is a prerequisite for seamless transitions.
Clarifying the Scenario
You: "A few quick questions. When you say crossfade, are we talking about the user-configurable crossfade (1-12 seconds), or the automatic gapless playback for album tracks? They work differently."
Interviewer: "Start with gapless playback, then explain crossfade."
You: "Got it. And mobile vs desktop? The audio APIs are quite different."
Interviewer: "Start with desktop, then talk about mobile constraints."
You: "Perfect. I will structure this in four parts. First, the audio pipeline from download to speaker. Second, the pre-buffering state machine that sets up the next track before the current one ends. Third, how the actual PCM crossfade blending works. Fourth, the gapless vs crossfade distinction and when each applies."
My Approach
I break this into four parts:
- The audio pipeline: How encoded audio travels from CDN to the speaker, and what data format is in the pipeline at each stage.
- Pre-buffering trigger: When does Spotify decide to start loading the next track, and how much does it load before the current song ends.
- The crossfade computation: What happens at the PCM level to blend two tracks together, including gain normalization.
- Gapless vs crossfade: The important distinction between album tracks (no audible overlap desired) and playlist transitions (user-configured overlap).
The key insight I lead with: crossfade happens at the raw PCM level, after decoding. You cannot crossfade compressed audio (Ogg Vorbis, AAC, MP3). You have to decode both tracks to PCM, then apply amplitude envelopes to each, then sum the two PCM buffers together.
The Architecture
Let me show the full pipeline from CDN download to speaker output. This diagram captures where each format lives in the pipeline.
Let me walk through this pipeline stage by stage.
Stage 1: Compressed download. Spotify stores audio as Ogg Vorbis (their primary format) or AAC. These files are hosted on Spotify's CDN. The client downloads them as chunked HTTP responses, writing compressed frames into a ring buffer. Spotify downloads in chunks (typically 128KB to 1MB at a time) rather than the entire file upfront, which is why you see a small lag when first playing a new track on a slow connection.
Stage 2: Decode to PCM. The Ogg Vorbis or AAC decoder processes compressed frames and produces raw Pulse Code Modulation (PCM) samples. PCM is uncompressed audio: each sample is a 16-bit or 32-bit floating-point value representing amplitude at an instant. At 44.1kHz stereo, one second of PCM is 44,100 samples x 2 channels x 2 bytes = 176KB. A 3-minute song as PCM is about 31MB. Spotify only decodes slightly ahead of playback (typically 1-2 seconds of PCM buffer), not the entire track.
Stage 3: Gain normalization. Before the PCM audio from Track B enters the crossfade engine, it must be volume-adjusted to match Track A's perceived loudness. Without this, the transition from a quiet acoustic track to a loud electronic track would be jarring regardless of how smooth the fade envelope is. I will cover this in the normalization deep dive.
Stage 4: Crossfade engine. During the overlap window, the engine reads from both decoded PCM buffers simultaneously. It applies a fade-out amplitude envelope to Track A (samples multiply by a decreasing coefficient) and a fade-in envelope to Track B (samples multiply by an increasing coefficient). The two resulting PCM buffers are summed sample by sample to produce the blended output.
Stage 5: Audio output. The blended PCM is written to the OS audio API (WASAPI on Windows, CoreAudio on macOS). The OS audio subsystem has its own small hardware buffer (10-50ms) that feeds the speaker hardware. This hardware buffer is what you hear.
Ogg Vorbis vs AAC on Spotify
Spotify primarily uses Ogg Vorbis for most clients because it is open and royalty-free. iOS clients use AAC due to Apple's hardware decoder pipeline. Both formats decode to the same PCM representation, so the crossfade engine is format-agnostic.
The Audio Pipeline and PCM Buffer Management
The pipeline has multiple buffers at different stages, each serving a different purpose. Understanding the buffer structure is essential to understanding how crossfade is possible without gaps.
The buffering state machine. The Spotify client maintains state about each track's download and decode position. The critical transition is from PLAYING to PRE_FETCH, which is triggered when the remaining playback time on Track A drops below a threshold.
When does pre-fetch trigger? The trigger threshold depends on the crossfade duration plus a safety margin. If the user has configured a 10-second crossfade, Spotify starts fetching Track B approximately 12-15 seconds before Track A ends. The client calculates remaining time from the track duration metadata and the current playback position.
How much does Spotify pre-fetch? For the crossfade case, Spotify needs the first N+2 seconds of Track B (where N is the crossfade duration) decoded into a PCM buffer before the overlap window begins. Downloading compressed audio is fast (a 10-second Ogg segment at 128kbps is only 160KB). The bigger concern is decode time: decoding Ogg Vorbis is CPU-intensive (relatively), so Spotify starts decoding early and parks a few seconds of Track B's PCM in a buffer, waiting for the crossfade window.
What if the download is slow? If Spotify cannot download and decode enough of Track B before the crossfade window begins, it falls back to a gap (a brief pause between tracks). This is why you sometimes hear a gap on a slow cellular connection even when crossfade theoretically should work.
Crossfade Math: Overlapping PCM Frames
The actual crossfade operation works at the sample level. This is where the math happens. I want to make this concrete because most descriptions hand-wave over it.
Linear crossfade. The simplest approach: Track A's amplitude multiplied by a coefficient that goes from 1.0 to 0.0 over the crossfade duration. Track B's amplitude multiplied by a coefficient that goes from 0.0 to 1.0. Sum the two results sample by sample. The problem: at the midpoint, both coefficients are 0.5. The summed energy at the midpoint is 0.5A + 0.5B. Even if A and B are at full amplitude, the sum is at half energy. This sounds like a slight dip or "V-shaped" volume drop in the middle of the crossfade. Listeners perceive this as an audible dip.
Equal-power crossfade. Use sinusoidal (cosine/sine) coefficients instead of linear. Mathematically: $\alpha(t) = \cos\left(\frac{t}{D} \cdot \frac{\pi}{2}\right)$ and $\beta(t) = \sin\left(\frac{t}{D} \cdot \frac{\pi}{2}\right)$. The important property: $\alpha^2 + \beta^2 = 1$ at all times. This means the total power of the mixed signal remains constant throughout the crossfade. There is no perceived volume dip. This is This is a common approach for a perceptually smooth crossfade; the exact curve remains an implementation choice.
Concrete gain values
For a normalized crossfade position u = t / D, the equal-power gains are alpha = cos(u * pi / 2) for Track A and beta = sin(u * pi / 2) for Track B. At 25% of the window the gains are approximately 0.924 and 0.383; at the midpoint they are both 0.707; at 75% they are approximately 0.383 and 0.924. These values make the constant-power property concrete without depending on a vendor-specific implementation.
Sample-level computation. For each audio frame during the crossfade window, the engine iterates over every PCM sample:
for each frame in crossfade_window:
t = elapsed_samples / sample_rate
alpha = cos(t / crossfade_duration * PI/2)
beta = sin(t / crossfade_duration * PI/2)
for each sample in frame:
output[i] = (track_a_pcm[i] * alpha) + (track_b_pcm[i] * beta)
At 44.1kHz stereo, this is 88,200 multiply-add operations per second during the crossfade window. On modern hardware, this is negligible. On a 2014 iPhone, this was a measurable CPU cost.
Gain normalization before crossfade. Before applying the amplitude envelope, both tracks' PCM buffers are adjusted by a pre-computed gain offset. Without normalization, a loud EDM track crossfading into a quiet folk recording would produce an unbalanced blend where one track overwhelms the other mid-crossfade.
Linear fades sound bad, equal-power fades sound right
The difference between linear and equal-power crossfade is not academic. If you implement a linear crossfade, listeners will hear a subtle "dip" in the middle of every transition. Equal-power curves are widely used in audio software because they reduce that midpoint dip.
Gapless Playback for Albums vs Crossfade for Playlists
These are two distinct features that solve different problems. Conflating them is a common mistake.
Gapless playback. For album tracks designed to flow continuously (for example, a live album or continuous mix), a player can use gapless playback rather than crossfade. The goal is zero audible gap between tracks without any overlap or volume change. This is harder than it sounds because audio encoders (Vorbis, AAC, MP3) add encoder delay at the beginning and padding at the end of each compressed file.
Encoder pre-gap and post-gap. A lossy encoder adds silence or delay at the beginning and padding at the end of a compressed file. The amount is codec- and encoder-specific; it must not be hard-coded as one universal sample count. Container or stream metadata can expose the delay and total content sample count, allowing a decoder to trim the leading padding from Track B and the trailing padding from Track A. The remaining PCM buffers can then be stitched at the intended sample boundary.
Crossfade. For playlist transitions (shuffled songs, curated playlists), the player applies the user-configured crossfade. The two tracks did not come from a continuous recording, so a brief overlap is acceptable or even desirable. Crossfade smooths the perceptual jump between songs with different keys, tempos, and sonic characteristics. The user configures the overlap window from 1 to 12 seconds in Settings.
The rule is: gapless is for tracks that share a continuous recording, while crossfade is for independent tracks. If continuity metadata indicates a gapless transition and crossfade is enabled, gapless should take priority for that boundary.
Mention the encoder pre-gap when discussing gapless
Describing encoder pre-gap trimming is what separates a strong candidate from one who says "gapless just plays tracks back to back." The reason there is any gap problem at all is the encoder latency artifact. Knowing that Vorbis and AAC both add silent samples and that the solution is metadata-guided trimming shows you understand audio encoding at a practical level.
The Tricky Parts
-
Seek operations during crossfade. If the user presses skip or seeks to a different position while a crossfade is in progress, the crossfade engine must immediately discard both PCM decode buffers, cancel the in-flight HTTP request for Track B, and start fresh from the new position. The state machine transitions must be clean and fast to avoid audible glitches on seek. Spotify handles this by treating seek as a hard reset of the pipeline.
-
Variable song length from the server. Audio files sometimes have incorrect duration metadata (rounding errors in the Ogg stream or CDN inconsistencies). If the client triggers the pre-fetch based on a stale duration estimate and the actual track is longer, the crossfade begins too early. Spotify tracks actual playback sample count rather than relying solely on duration metadata, recalculating remaining time continuously.
-
Background audio on iOS. iOS's CoreAudio requires the app to hold an AVAudioSession background entitlement to continue playing audio when backgrounded. The crossfade engine runs in this audio callback thread, which has strict timing requirements (must return in under the buffer period, typically 10ms). If the crossfade compute takes longer than 10ms (unlikely on modern hardware, possible on older devices during a track transition where both decoders are initializing), the audio output buffer underruns and you hear a click or pop.
-
Network handoff during pre-fetch. On mobile, switching from Wi-Fi to cellular mid-download interrupts the compressed audio chunk download. The HTTP client must seamlessly resume the range request from the last received byte. If the handoff causes a delay that pushes the chunk delivery past the pre-fetch deadline, Spotify falls back to a gap rather than attempting a partial crossfade.
-
Replay Gain metadata vs album normalization. Spotify applies loudness normalization at two levels: track-level normalization (each track sounds roughly the same loudness) and album-level normalization (tracks within an album maintain their relative volume relationships). The default for most users is track normalization. Users who prefer quiet late-night listening can engage the "Quiet" loudness level. During crossfade, the gain offset must be pre-applied to both tracks' PCM buffers before the amplitude envelopes are calculated.
What Most People Get Wrong
| Mistake | What they say | Why it is wrong | What to say instead |
|---|---|---|---|
| Wrong domain | "This is a server-side streaming feature" | Crossfade is entirely client-side. The server just provides compressed audio chunks | "The crossfade engine runs on your device. The server sends compressed audio; the client decodes to PCM and blends in the audio callback thread" |
| Wrong buffer level | "Spotify pre-loads the compressed Ogg file" | Pre-loading compressed is insufficient; you need decoded PCM in memory at the time of crossfade | "Spotify decodes a few seconds of Track B into a PCM buffer ahead of time, so the crossfade engine has raw samples to blend immediately" |
| Ignoring gain normalization | "Just fade the volume of each track" | Without gain normalization, a loud track fading into a quiet track sounds unbalanced mid-crossfade | "ReplayGain or Spotify's loudness normalization pre-adjusts track gain to a target LUFS before the crossfade amplitude envelope is applied" |
| Linear fade | "Multiply one track by a decreasing coefficient" | Linear fades produce a perceptible volume dip at the midpoint (half amplitude = -6dB loss) | "Equal-power cosine/sine crossfade: coefficients satisfy cos squared + sin squared = 1, ensuring constant perceived loudness" |
| Conflating gapless with crossfade | "Gapless and crossfade are the same feature" | Gapless = sample-accurate stitch with encoder pre-gap trimming, no overlap. Crossfade = user-configured overlap window with amplitude blending | "Different modes for different use cases: gapless for albums, crossfade for playlists" |
How I Would Communicate This in an Interview
Here is how I would actually say this, about 90 seconds:
"Spotify crossfade is entirely client-side. The server just serves compressed audio chunks. All the interesting work happens on your device.
Here is the timeline: when you are, say, 15 seconds from the end of Track A (with a 10-second crossfade configured), Spotify fires an HTTP range request for the first few seconds of Track B. It downloads the compressed Ogg Vorbis chunks, decodes them into a PCM buffer, and applies a gain normalization offset so both tracks are at the same perceived loudness.
When the crossfade window opens, the audio engine reads from two PCM decode buffers simultaneously. It applies equal-power amplitude envelopes: Track A's samples are multiplied by cos(t/D times pi/2) and Track B's samples by sin(t/D times pi/2). These two PCM streams are summed sample by sample. The property of equal-power crossfade is that cos squared plus sin squared equals 1, so the total signal power stays constant throughout the blend, which means the listener hears no volume dip.
For album tracks where you want gapless playback instead of crossfade, Spotify reads encoder pre-gap metadata from the Ogg stream header and trims those exact leading samples from Track B. This achieves sample-accurate stitching with no audible gap and no overlap, even when the encoder has added silence padding to each file."
The equal-power formula is the standout answer
If you can say "cosine and sine coefficients where alpha squared plus beta squared equals one" in your answer, you have immediately differentiated yourself from every candidate who said "fade the volume." That one mathematical property is the entire reason equal-power crossfade sounds seamless while linear crossfade does not.
Interview Cheat Sheet
- Crossfade is client-side: The server serves compressed audio. All blending happens in the Spotify app's audio processing pipeline on your device.
- PCM is the only blendable format: You cannot crossfade Ogg Vorbis or AAC directly. Both tracks must be decoded to raw PCM before any amplitude math.
- Pre-fetch trigger: Spotify starts downloading the next track when remaining playback time drops below crossfade_duration + safety_margin (approximately N+5 seconds).
- Equal-power crossfade: Use cosine/sine coefficients where alpha squared plus beta squared equals 1 at all times, ensuring constant perceived loudness with no midpoint dip.
- Linear crossfade is wrong: Linear coefficients sum to 1.0 at midpoint by amplitude (0.5 + 0.5) but only 0.5 by power, causing a perceived -6dB dip.
- ReplayGain normalization: Both tracks' PCM gain must be adjusted to the same target LUFS before the crossfade envelope is applied, or volume jumps ruin the transition.
- Gapless vs crossfade: Gapless trims encoder pre-gap samples using Ogg metadata for sample-accurate stitching. Crossfade applies a user-configured overlap with amplitude blending.
- Encoder pre-gap: Ogg Vorbis encoders add ~576 leading silence samples due to MDCT encoder latency. Gapless playback trims these using stream header metadata.
- iOS constraint: The crossfade compute runs in the CoreAudio callback thread, which has a hard deadline of approximately 10ms. Both decoders must be initialized before the crossfade window, not during it.
- Seek handling: Any user seek or skip during crossfade immediately cancels the blending, discards both PCM buffers, and restarts the pipeline from the new position.
Test Your Understanding
Quick Recap
- Crossfade is entirely client-side: the server sends compressed audio chunks; all decoding, normalization, and blending happen in the Spotify app's audio pipeline.
- Both tracks must be decoded to raw PCM before blending, because you cannot apply amplitude math to compressed Ogg Vorbis or AAC frames.
- Pre-fetch triggers approximately crossfade_duration plus 5 seconds before Track A ends, downloading and decoding enough of Track B to fill the PCM overlap buffer.
- Equal-power crossfade uses cosine/sine coefficients satisfying alpha squared plus beta squared equals 1, maintaining constant perceived loudness with no midpoint dip.
- ReplayGain loudness normalization adjusts each track's PCM gain to a common target LUFS before the crossfade envelope is applied, ensuring balanced blending.
- Gapless playback for albums requires reading encoder pre-gap sample counts from Ogg stream headers and trimming exactly those samples from the track boundary.
- On iOS, both PCM decode buffers must be fully initialized before the CoreAudio callback fires, as the real-time audio thread cannot block to wait for decode.
- Automix extends crossfade with BPM-based tempo matching and beat alignment, using server-pre-analyzed tempo metadata and client-side phase vocoder time-stretching.
Related Concepts
- How Spotify Plays Music Instantly: The same chunked HTTP download and CDN infrastructure that enables fast playback start also enables the pre-fetch that crossfade depends on.
- How Spotify Recommendations Work: The recommendation engine determines what Track B is. Crossfade depends on accurate next-track prediction happening before the current track ends.
- How Data Compression Works: Understanding why Ogg Vorbis adds encoder pre-gap and why you cannot crossfade compressed audio frames requires understanding how lossy audio codecs encode and decode data.
Related Articles
How Spotify uses predictive prefetching, Vorbis/AAC codec switching, local cache management, and crossfade buffering for instant playback.
Spotify's recommendation engine combines collaborative filtering (matrix factorization on listening history), NLP on playlist metadata, and audio analysis. Learn how Discover Weekly and Daily Mixes are generated at scale.
Understand the algorithms behind gzip, zstd, and LZ4 that compress files and network payloads, the entropy theory, dictionary-based compression, and why system designers must understand when to compress, when not to, and what the tradeoffs are.