How Folia's Playback Sync Bridge Works Between Windows
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 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.
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.
// 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 bybuildRemoteControlSnapshotFromPlaybackSyncBridgepublishDiscordPresenceSnapshot– Transmits Discord Rich Presence datapublishStagePlayerSnapshot– Dispatches a StagePlayerSnapshot viabuildStagePlayerSnapshotFromPlaybackSyncBridge
// 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 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:
// 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.
// 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 comprehensivePlaybackSyncBridgeModeland pushes snapshots viawindow.electronIPC APIs. - The utility layer (
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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →