Skip to content
M4rulliPublic

About

A lightweight browser extension for watching videos together, in sync.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

Repository files navigation

SyncParty

Python FastAPI Cloudflare Workers

Watch together. Stay in sync.

SyncParty synchronizing playback across two browsers

SyncParty keeps video playback synchronized across independent browsers. Each participant watches on the original platform while SyncParty exchanges only playback events such as play, pause and seek.

The browser extension is designed to automatically detect a supported player on the current page and prepare the appropriate integration, so users do not need to manually choose between providers such as YouTube, Netflix, Prime Video or standard HTML5 video.

Since every browser owns its own player and communicates over the network, SyncParty is a small distributed system: clients have independent state, different clocks and variable network latency.

SyncParty never streams, proxies, downloads, decrypts or redistributes video content. It does not bypass subscriptions, DRM, paywalls or provider security controls.

How it works

Each browser keeps playing its own copy of the video. SyncParty exchanges only playback state through an authoritative room, which keeps every participant aligned without transmitting or proxying the video itself.

The extension follows a small lifecycle: it detects a compatible player, becomes ready to create or join a room, waits for another participant, and enters the synchronized state. Temporary connection failures are handled through reconnection and state reconciliation.

SyncParty extension state machine

Architecture

SyncParty is designed around a browser extension, an HTTP API and one stateful room actor per room.

The browser extension will observe and control the local video player. HTTP is used to create and join rooms, while WebSockets provide the realtime bidirectional channel used for playback synchronization.

Each room is implemented as a Cloudflare Durable Object and behaves as a stateful actor: it has a stable identity, owns the authoritative room state and processes commands addressed to that room.

Clients never modify shared state directly. They send playback commands to the room actor, which validates them, applies the domain transition, assigns the next room sequence number to establish a single shared event order, persists the new state and broadcasts the resulting event.

sequenceDiagram
    participant A as Client A
    participant R as Room Actor
    participant B as Client B

    A->>R: CONNECT(ticket)
    R-->>A: STATE(roomSequence, playback, participants, serverTime)

    B->>R: CONNECT(ticket)
    R-->>B: STATE(roomSequence, playback, participants, serverTime)

    B->>R: PLAY(clientSequence, position)
    R->>R: Validate → transition → persist
    R-->>A: PLAY(roomSequence, serverTime)
    R-->>B: PLAY(roomSequence, serverTime)

    A->>R: PING(T1)
    R-->>A: PONG(T1, serverTime)

    opt Connection drops
        A->>R: RECONNECT(new ticket)
        R-->>A: STATE(latest state)
    end
Loading

Realtime protocol

Playback commands carry a monotonically increasing clientSequence, used to reject duplicate or stale commands from the same participant.

Every accepted playback command also receives a monotonically increasing roomSequence. This number defines the official order of events inside the room, so every client can agree on which action happened before or after another.

Direction Types
Client → Server PLAY, PAUSE, SEEK, PING
Server → Client PLAY, PAUSE, SEEK, STATE, PONG, ERROR

PING / PONG expose timing information used to estimate round-trip latency and clock offset.

Browser extension

The frontend is a lightweight browser extension designed to require as little configuration as possible.

When opened on a supported page, the extension automatically looks for a controllable player. As more adapters become available, the same detection flow will select the appropriate integration without asking the user to choose a provider.

The extension is designed to support multiple browsers over time. Browser-specific APIs are isolated behind a small abstraction so that the synchronization logic remains independent from Chrome, Firefox or other WebExtensions-compatible browsers.

Target extension flow from player ready to synchronized

Implemented room flow from player detection to an active synchronized room.

The synchronization engine depends on PlayerAdapter and a typed RoomPort. Browser-specific operations remain behind BrowserAPI, while WebSocket lifecycle and runtime protocol validation remain inside the realtime transport boundary.

Player adapters follow the same principle: they isolate player detection and playback controls from the synchronization logic. Provider-specific selectors and behavior remain inside the corresponding adapter, so a platform change does not affect the realtime protocol or room logic.

The Sync Engine coordinates room state, realtime events and authoritative ordering. It communicates through RoomPort and controls the local player exclusively through PlayerAdapter.

The current implementation targets Chromium-based browsers and standard HTML5 <video> playback, with additional browser and provider adapters added where reliable integrations are available.

Provider names are used only to describe possible compatibility. SyncParty is not affiliated with, sponsored by or endorsed by those platforms.

Security

SyncParty uses standard transport encryption and short-lived room credentials rather than implementing a custom cryptographic protocol.

In production, all communication with the backend uses HTTPS or WSS, so room creation, joining and realtime synchronization are protected in transit by TLS.

The extension presents room invitations as copyable room codes. A room code represents the room identifier and invitation capability needed to request access. After validating that capability, the backend issues a short-lived, one-time room ticket for the participant. The ticket is then used to establish the realtime WebSocket connection.

Room tickets are scoped to the room that issued them, expire after at most 60 seconds and are consumed by the first connection attempt. They cannot be used as permanent credentials. Only capability and ticket verifiers are persisted; raw credentials must never be logged.

Status

The backend is currently experimental.

Room creation, joining, WebSocket synchronization, authoritative room sequencing, state snapshots, reconnect through a new ticket, room expiry and basic abuse limits are implemented.

The Chromium Manifest V3 extension implements HTML5 player detection, room creation and joining, realtime synchronization, authoritative ordering, participant presence and automatic reconnect through fresh tickets. Additional browser and provider adapters are not yet implemented.

Development

cd backend
npm install
uv run pywrangler dev

Run tests and linting with:

uv run pytest
uv run ruff check .

Build the browser extension with:

cd extension
npm install
npm test
npm run typecheck
npm run build

Load extension/public as an unpacked extension in a Chromium-based browser. Reload any already-open video page after installing or rebuilding the extension.

License

SyncParty is licensed under the GNU General Public License v3.0.

About

A lightweight browser extension for watching videos together, in sync.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages