# Music Assistant Flow Mode vs Gapless Playback: How the Server Handles Continuous Audio Streaming

> Understand Music Assistant flow mode vs gapless playback. Learn how the server streams continuous audio for uninterrupted listening, even when hardware lacks gapless capabilities.

- Repository: [Music Assistant/server](https://github.com/music-assistant/server)
- Tags: deep-dive
- Published: 2026-06-21

---

**Music Assistant uses native gapless playback when your player supports track stitching, but falls back to Flow Mode—where the server concatenates tracks into a single PCM stream—when hardware lacks gapless capabilities or when crossfade is required.**

The music-assistant/server repository distinguishes between two approaches for continuous audio playback. Gapless playback delegates track transitions to the player hardware, while Flow Mode processes the entire queue server-side into one continuous stream. Understanding when each activates helps optimize audio quality and resource usage in your multi-room setup.

## What Is Gapless Playback in Music Assistant?

Gapless playback occurs when the player device itself stitches the next audio frame to the previous one without silence. In the Music Assistant architecture, this happens when a player reports `supports_gapless == True` through the `Player` model defined in [`music_assistant/models/player.py`](https://github.com/music-assistant/server/blob/main/music_assistant/models/player.py).

When gapless playback is active:
- Music Assistant sends each track individually to the player
- The device handles all transition logic natively
- No server-side audio processing or resampling occurs
- Crossfade capabilities depend entirely on player hardware

This approach minimizes server CPU usage and preserves bit-perfect audio when the player supports it.

## What Is Flow Mode and When Does It Activate?

Flow Mode is a server-side fallback that creates a single continuous audio stream from the entire playback queue. The system reads tracks sequentially, optionally resamples them, and concatenates them into one PCM stream sent to the player.

Flow Mode automatically activates in three scenarios:
1. The player returns `True` from `requires_flow_mode()` (indicating no native gapless support)
2. Crossfade is requested but the player cannot handle different sample rates between tracks
3. The user explicitly forces Flow Mode via configuration

### The Decision Logic in the Stream Controller

The core determination happens in [`music_assistant/controllers/streams/controller.py`](https://github.com/music-assistant/server/blob/main/music_assistant/controllers/streams/controller.py). The controller evaluates multiple conditions to set the `flow_mode` boolean:

```python

# music_assistant/controllers/streams/controller.py

crossfade_needs_flow_mode = (
    not self.config.get_value(CONF_CROSSFADE_DIFFERENT_SAMPLE_RATES, False)
    and not protocol_player.supports_gapless
)
flow_mode = (
    force_flow_mode
    or (protocol_player is not None and protocol_player.flow_mode)
    or crossfade_needs_flow_mode
)

```

When `flow_mode` evaluates to `True`, the controller passes the queue to [`controllers/streams/audio.py`](https://github.com/music-assistant/server/blob/main/controllers/streams/audio.py) to build the continuous stream. The system also maintains a debug log of individual tracks in `queue.flow_mode_stream_log` for troubleshooting.

### Sample Rate Handling Strategies

Flow Mode requires the entire queue to share a single sample rate. The behavior is controlled by the `flow_mode_sample_rate` configuration entry defined in [`music_assistant/constants.py`](https://github.com/music-assistant/server/blob/main/music_assistant/constants.py).

The available strategies include:

- **Smart** (default): Starts with the first track's sample rate, upsamples lower-rate tracks, and restarts the flow when encountering a higher-rate track
- **Bit-perfect**: Never resamples; playback restarts whenever sample rates differ (disables gapless and crossfade)
- **Fixed rates (48 kHz / 96 kHz)**: Forces all audio to the specified rate using high-quality resamplers
- **Highest**: Resamples to the maximum sample rate the player supports

The configuration is read in [`controllers/streams/audio.py`](https://github.com/music-assistant/server/blob/main/controllers/streams/audio.py) (lines 1206-1238):

```python

# music_assistant/controllers/streams/audio.py

flow_mode_conf = player.config.get_value(
    CONF_FLOW_MODE_SAMPLE_RATE, FLOW_MODE_SAMPLE_RATE_SMART
)

```

## How Player Capabilities Determine the Mode

Each player implementation in `music_assistant/providers/*/player.py` defines a `requires_flow_mode()` method thatsignals whether the hardware needs server-side stream concatenation.

### The requires_flow_mode Property

Player providers override this method to indicate their gapless capabilities:

**Amplipi** (lacks native gapless support):

```python

# music_assistant/providers/amplipi/player.py

def requires_flow_mode(self) -> bool:
    return True

```

**MSX Bridge** (handles per-track playback natively):

```python

# music_assistant/providers/msx_bridge/player.py

def requires_flow_mode(self) -> bool:
    return False

```

The stream controller queries this property via `protocol_player.flow_mode` to determine whether to enable Flow Mode automatically.

## Configuring Flow Mode Behavior

End users control Flow Mode through the "Enable queue flow mode" toggle in the UI. This setting corresponds to translation keys in [`music_assistant/translations/en.json`](https://github.com/music-assistant/server/blob/main/music_assistant/translations/en.json) under `common.config_entries.flow_mode`.

When enabled, this setting forces Flow Mode regardless of the player's native capabilities, bypassing the automatic `supports_gapless` detection.

## Practical Implementation Examples

### Enabling Flow Mode via API

You can programmatically enable Flow Mode for a specific player using the configuration API:

```python

# Example: turn on flow mode for player "living_room_speaker"

await mass.players.set_config_value(
    player_id="living_room_speaker",
    key="flow_mode",
    value=True,
)

```

This updates the per-player configuration file, which the stream controller reads during playback initialization.

### Forcing Flow Mode for Single Requests

For custom services or one-off streams, you can bypass the capability check entirely:

```python

# Example: play a URL, forcing flow mode

await mass.streams.queue_uri(
    uri="https://example.com/track.mp3",
    player_id="kitchen_speaker",
    force_flow_mode=True,
)

```

The `force_flow_mode=True` parameter (see line 1028 in [`controller.py`](https://github.com/music-assistant/server/blob/main/controller.py)) overrides automatic detection and forces the controller to build a continuous stream.

### Debugging with Flow Mode Logs

When troubleshooting continuous playback issues, inspect the internal track log:

```python

# After playback, retrieve the per-track log

log = await mass.player_queues.get_queue("living_room_speaker")
print(log.flow_mode_stream_log)   # list of dicts with track metadata

```

This `flow_mode_stream_log` property is populated in [`controllers/streams/audio.py`](https://github.com/music-assistant/server/blob/main/controllers/streams/audio.py) (line 2064) whenever Flow Mode is active, showing exactly which tracks were concatenated and their metadata.

## Summary

- **Gapless playback** delegates track stitching to the player hardware when `supports_gapless` is `True`, minimizing server processing
- **Flow Mode** concatenates tracks server-side into a single PCM stream when `requires_flow_mode()` returns `True` or when crossfade is needed
- The **stream controller** in [`music_assistant/controllers/streams/controller.py`](https://github.com/music-assistant/server/blob/main/music_assistant/controllers/streams/controller.py) automatically selects the appropriate mode based on player capabilities and configuration
- **Sample rate handling** in Flow Mode offers five strategies from bit-perfect to fixed 96 kHz resampling
- You can **force Flow Mode** via the UI toggle or API calls when specific use cases require server-side stream processing

## Frequently Asked Questions

### What is the difference between Flow Mode and gapless playback in Music Assistant?

Gapless playback relies on the player device to stitch tracks together without silence, while Flow Mode has the Music Assistant server concatenate tracks into a single continuous stream before sending them to the player. Flow Mode is automatically used when the player lacks native gapless support or when crossfade is required between tracks with different sample rates.

### How do I know if my player is using Flow Mode or native gapless playback?

Check the player's `requires_flow_mode()` implementation in its provider file under `music_assistant/providers/`. If this method returns `True`, Music Assistant automatically enables Flow Mode. You can also inspect the `flow_mode_stream_log` property of the queue after playback to see if the server was concatenating tracks.

### Why does Flow Mode require all tracks to use the same sample rate?

Because Flow Mode creates a single continuous PCM stream, the audio format must remain consistent throughout the entire queue. The server handles this through the `flow_mode_sample_rate` configuration, which can either resample all tracks to a common rate (Smart, 48kHz, 96kHz, or Highest) or restart the stream when rates differ (Bit-perfect).

### Can I force Flow Mode even if my player supports gapless playback?

Yes. Enable the "Enable queue flow mode" toggle in the player configuration UI, or use the API call `mass.players.set_config_value(player_id="your_player", key="flow_mode", value=True)`. You can also pass `force_flow_mode=True` to individual stream requests like `queue_uri()` for one-time enforcement.