Beatsync Documentation

repository·main·Indexed 25 days ago

https://github.com/freeman-jiang/beatsync

A high-precision web audio player for synchronized multi-device playback using NTP-inspired time synchronization. Features include millisecond-accurate sync, spatial audio capabilities, and a self-hostable architecture consisting of a Bun-based server, a Next.js client, and a shared package for type-safe schemas.

Tokens
13.8K
Snippets
26
Records
110
Agent score
84%

What's inside beatsync

  1. Overview of Beatsync

    main
    Beatsync is a high-precision web audio player designed for multi-device playback. It features millisecond-accurate synchronization using NTP-inspired time synchronization primitives, spatial audio capabilities (controlling device volumes via a virtual listening source), and a polished UI. It is cross-platform and works on any modern browser, though Chrome is recommended for optimal performance. The project is self-hostable.
  2. Quickstart: Set up and run Beatsync locally

    main

    Beatsync uses Turborepo for monorepo management. To run the project locally, follow these steps:

    1. Configure Environment Variables: In apps/client, create or update a .env file with the following values:

      • NEXT_PUBLIC_API_URL: The URL for the HTTP server.
      • NEXT_PUBLIC_WS_URL: The URL for the WebSocket server.
    2. Install Dependencies: Use bun install to install dependencies for all workspaces.

    3. Start the Application: Use bun dev to start both the client (on port 3000) and the server (on port 8080).

    # 1. Configure apps/client/.env
    NEXT_PUBLIC_API_URL=http://localhost:8080
    NEXT_PUBLIC_WS_URL=ws://localhost:8080/ws
    
    # 2. Install and run
    bun install
    bun dev
  3. Implement the 3-step R2 upload flow

    main

    The upload process has changed from a single multipart/form-data POST to a three-step coordination flow using presigned URLs. This reduces server bandwidth by allowing direct client-to-R2 uploads.

    1. Request URL: Client requests a presigned upload URL from the server via POST /api/upload-url.
    2. Direct Upload: Client uploads the audio file directly to R2 using the provided presigned URL.
    3. Confirm Upload: Client notifies the server of the successful upload via POST /api/upload-complete to trigger WebSocket room updates.
  4. Configure Cloudflare R2 for Beatsync Server

    main

    To use the R2 integration, you must configure the following environment variables in apps/server/.env. This setup replaces the previous filesystem-based storage with Cloudflare R2 for audio files.

    # Required environment variables in apps/server/.env
    CLOUDFLARE_ACCOUNT_ID=your_cloudflare_account_id
    CLOUDFLARE_R2_ACCESS_KEY_ID=your_r2_access_key_id
    CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_r2_secret_access_key
    CLOUDFLARE_R2_BUCKET_NAME=beatsync-audio
  5. Synchronize client time using NTP-inspired probe pairs

    main

    The ntp.ts utility provides primitives for millisecond-accurate time synchronization between a client and server using a 'coded probe pair' (Huygens) method. This method sends two NTP requests with a known inter-departure gap to filter out measurements corrupted by network queuing, TCP head-of-line blocking, or garbage collection pauses.

    To perform synchronization:

    1. Call sendProbePair to dispatch two requests via WebSocket.
    2. Use handleNTPResponse to process incoming server responses.
    3. handleNTPResponse will return an NTPMeasurement only when a complete, 'pure' pair (where the server inter-arrival gap matches the client inter-departure gap within NTP_CONSTANTS.PROBE_GAP_TOLERANCE_MS) is validated.
  6. Manage audio playback synchronization

    main

    The RoomManager handles synchronized audio playback by coordinating audio loading across all clients before execution.

    1. Initiate Loading: Call initiateAudioSourceLoad to broadcast a LOAD_AUDIO_SOURCE event. This starts a 3-second timeout to ensure playback proceeds even if some clients are slow.
    2. Process Client Readiness: As clients load the source, call processClientLoadedAudioSource.
    3. Execution: Once all clients have reported readiness (or the timeout is reached), executeScheduledPlay is called to broadcast the SCHEDULED_ACTION with the calculated execution time.
  7. Use the new R2-compatible server endpoints

    main

    The server API has been updated to support the R2 architecture. Note that the original /upload endpoint is deprecated and will return a deprecation error message.

    EndpointMethodDescription
    /api/upload-urlPOSTGenerates presigned upload URLs
    /api/upload-completePOSTConfirms successful uploads
    /audioPOSTRedirects to R2 public URLs (replaces direct file serving)
    /uploadPOSTDeprecated: Returns a helpful error message
  8. Configure PM2 for the Beatsync server

    main

    To run the Beatsync server using PM2, use the following configuration in pm2.config.js. This setup ensures the server runs from the correct directory using the bundled entry point and utilizes the bun interpreter resolved via mise shims to maintain version consistency with the repository's mise.toml pin.

    module.exports = {
      name: "beatsync-server", // Name of your application
      cwd: "apps/server",
      script: "dist/index.js", // Bundled entry point
      // Resolve bun through mise shims so the version comes from this repo's
      // mise.toml pin, not a stale standalone install (~/.bun/bin/bun).
      interpreter: `${process.env.HOME}/.local/share/mise/shims/bun`,
    };