# How Folia's Playback Sync Bridge Works Between Windows

> Discover how Folia's playback sync bridge synchronizes multiple windows. Learn about its centralized React UI model and Electron IPC distribution for seamless playback across your setup.

- Repository: [冬霧/folia-major](https://github.com/chthollyphile/folia-major)
- Tags: internals
- Published: 2026-07-06

---

**Folia's playback sync bridge maintains synchronization across multiple windows by aggregating playback state into a centralized model in the React UI and distributing snapshots via Electron IPC to the main process, remote-control window, and Stage player.**

The playback sync bridge in [chthollyphile/folia-major](https://github.com/chthollyphile/folia-major) is the architectural glue that keeps the main interface, remote-control window, and optional Stage player in perfect lock-step. This Electron-based system ensures that playback actions in one window instantly propagate to all others through a structured snapshot-based communication protocol.

## Building the Playback Sync Bridge Model

The synchronization process begins in the React layer with the `useElectronPlaybackBridge` hook. This hook constructs a comprehensive state representation by calling `buildPlaybackSyncBridgeModel` from [`src/utils/playbackSyncBridge.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/utils/playbackSyncBridge.ts).

The model aggregates everything a playback UI can expose:

- Current song metadata and queue state
- Precise timestamps (`currentTimeSec`, `durationSec`, `stagePositionSec`)
- Player state (playing, paused, loop mode)
- UI flags (transparent mode, click-through, window dimensions)
- Export state and daylight preferences

Helper functions `getPlaybackSyncBridgeArtist` and `getPlaybackSyncBridgeCoverUrl` normalize artist and cover data across local files, Navidrome servers, and remote sources before inclusion in the model.

```tsx
// src/hooks/useElectronPlaybackBridge.ts (excerpt)
const model = buildPlaybackSyncBridgeModel({
  activePlaybackContext,
  currentSong,
  playQueue,
  currentTimeSec,
  stagePositionSec,
  durationSec: duration,
  stageDurationSec,
  playerState,
  coverUrl,
  cachedCoverUrl,
  effectiveLoopMode,
  isFmMode,
  isStageActive: isNowPlayingStageActive,
  controlsDisabled: isNowPlayingControlDisabledRef.current,
  transparentModeEnabled: transparentPlayerBackground,
  mainWindowClickThroughEnabled,
  mainWindowBorderVisible: showTransparentWindowBorder,
  playerChromeHidden: isPlayerChromeHidden,
  exportState,
  isDaylight,
  isLiked,
  lyricOffsetMs: lyricTimelineOffsetMs,
  mainWindowWidth: window.innerWidth,
  mainWindowHeight: window.innerHeight,
});

```

## IPC Communication and Snapshot Publishing

Once the model is built, the hook publishes specialized snapshots through the Electron preload script's injected APIs. The `window.electron` object exposes three critical publishing functions that map the shared model to wire-format specific to each consumer:

- **`publishRemoteControlSnapshot`** – Sends a **RemoteControlSnapshot** built by `buildRemoteControlSnapshotFromPlaybackSyncBridge`
- **`publishDiscordPresenceSnapshot`** – Transmits Discord Rich Presence data
- **`publishStagePlayerSnapshot`** – Dispatches a **StagePlayerSnapshot** via `buildStagePlayerSnapshotFromPlaybackSyncBridge`

```tsx
// src/hooks/useElectronPlaybackBridge.ts
const publishRemote = () => {
  if (!window.electron?.publishRemoteControlSnapshot) return;
  void window.electron.publishRemoteControlSnapshot(
    buildRemoteControlSnapshotFromPlaybackSyncBridge(model, {
      includeLyrics: true,
      lyrics,
    })
  );
};

```

These functions live in [`src/utils/playbackSyncBridge.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/utils/playbackSyncBridge.ts) and are called periodically to ensure all windows receive fresh state.

## Main Process Coordination

The Electron main process (`electron/main.cjs`) serves as the central hub for playback sync bridge operations. It maintains bridge status through `buildPlaybackSyncBridgeStatus()`, which tracks whether the remote-control window is open (`remoteControlOpen`) and whether Discord Rich Presence is enabled.

### Broadcasting Status Changes

The main process broadcasts bridge status to all renderer windows via IPC:

```js
// electron/main.cjs
mainWindow.webContents.send('playback-sync-bridge-status-changed', status);

```

This `broadcastPlaybackSyncBridgeStatus` function ensures that new windows immediately know the current synchronization state.

### Windows Taskbar Integration

The main process also extracts minimal control flags (`hasActiveTrack`, `canGoPrevious`, `canGoNext`, `isPlaying`) using `buildTaskbarControlsFromPlaybackSyncBridge` and updates native Windows thumbar buttons through `updateWindowThumbarButtons`, allowing media control directly from the taskbar without opening the main window.

## Cross-Window Synchronization

### Remote Control Window Updates

When the remote-control window opens, it queries the main process for the latest snapshot stored in `latestRemoteControlSnapshot`. It then registers a listener for the `'playback-sync-bridge-status-changed'` event to receive real-time updates.

```js
// electron/main.cjs (excerpt)
function broadcastRemoteControlSnapshot(snapshot) {
  latestRemoteControlSnapshot = snapshot;
  if (remoteControlWindow && !remoteControlWindow.isDestroyed()) {
    remoteControlWindow.webContents.send('remote-control-snapshot-changed', snapshot);
  }
}

```

This mechanism keeps the remote UI synchronized even when the user switches to Stage mode or minimizes the main application.

### Stage Player Integration

For Stage mode, the hook calls `buildStagePlayerSnapshotFromPlaybackSyncBridge` to produce a `StagePlayerSnapshot` containing the current queue, cover art, and precise timing data (`positionMs`, `durationMs`). The main process forwards this to the Stage API (`createStageApi`) so external clients can monitor and control Stage playback. Hand-off logic between windows is handled in `electron/windowPlaybackHandoff.cjs`.

## Summary

- **The React layer** (`useElectronPlaybackBridge`) builds a comprehensive `PlaybackSyncBridgeModel` and pushes snapshots via `window.electron` IPC APIs.
- **The utility layer** ([`src/utils/playbackSyncBridge.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/utils/playbackSyncBridge.ts)) defines model structures, conversion helpers, and snapshot builders for remote control, Discord presence, taskbar controls, and Stage player.
- **The main process** (`electron/main.cjs`) stores the latest remote-control state, broadcasts bridge status changes, updates native UI elements (taskbar/thumbar), and manages window-specific snapshots.
- **Secondary windows** subscribe to bridge status events and retrieve snapshots to maintain synchronization with the main player without direct React context sharing.

## Frequently Asked Questions

### What is the playback sync bridge in Folia?

The playback sync bridge is an IPC-based state management system that aggregates playback data from the main React UI and distributes it to auxiliary windows (remote control, Stage player) and system integrations (Discord, Windows taskbar). It ensures all components reflect the same playback state simultaneously.

### How does Folia keep the remote control window synchronized?

The remote control window receives updates through the `remote-control-snapshot-changed` IPC event broadcast from the main process. When opened, it retrieves the current `latestRemoteControlSnapshot` and listens for subsequent changes, ensuring it displays the active track, queue position, and playback status matching the main window.

### What data does the playback sync bridge transmit?

The bridge transmits a comprehensive model including current song metadata, playback timestamps, queue state, loop modes, UI configuration flags (transparency, click-through), window dimensions, and lyric offsets. Specialized snapshots derived from this model send only relevant subsets to each consumer (remote controls receive lyrics, taskbar receives control flags, etc.).

### How does the Stage player receive playback state?

The Stage player receives state through `publishStagePlayerSnapshot`, which creates a `StagePlayerSnapshot` containing queue data, cover URLs, and precise timing information. The main process forwards this to the Stage API via `createStageApi`, allowing external Stage clients to monitor and control playback remotely.