Skip to content

Multiplayer B: shared session — synchronized launches & song arrangement #1

Description

@spacedevin

Summary

Multiplayer model A (independent b2b decks) just shipped: each player drives their own session/song locally, and the co-DJ stream now reflects the armed/launched pattern (emitLivePlayback) rather than the edit rack, so a collaborator hears your launches on your deck lane and the crossfader blends them.

Model B is the next step: a shared session where launches and song position are synchronized across players — when the host (or a master lane) launches a pattern/scene, everyone follows one playhead. This is necessary for the session system to work "properly" in a true collaborative jam (not just two independent decks).

Why A is not enough

The live-performance layer is local runtime state (rt.*), never streamed as control state:

State Where Synced today
Song position (rt.songScene, songSceneEndTick) runtime
Launched pattern (rt.sessionArmedClipIds) runtime ❌ (only the resulting audio/pattern streams via A)
Per-track overrides (rt.trackOverrides) runtime
Patterns / clips / session grid / scenes project → TPL ✅ (master-gated, Merge.tish)

In A we stream the realized pattern (the notes), so the other side hears the result but doesn't share the intent — they can't follow your song, and there's no single shared arrangement/playhead. The song arrangement is also still implicit (saved scenes in grid order); there's no explicit, editable, portable arrangement.

Proposed architecture for B

  1. Explicit song arrangement block in TPL — make the arrangement data, not derived:

    song
      P1 x2
      P2
      P3 x4
    

    Emit + parse + round-trip + tests. The song-chain in playbackTick reads this instead of deriving grid order. (Foundation: already discussed as a standalone improvement.)

  2. Launch / transport control messages on the hub — a new message type alongside tpl.block:

    • launch.scene { sceneIdx, effectivePerfStep }
    • launch.clip { trackId, clipId, effectivePerfStep } (per-track override)
    • transport { play: "song"|"sequence"|"stop", songScene, effectivePerfStep }
      Gated by master rights (laneCanMaster in Merge.tish); per-track overrides scoped to the track owner.
  3. Shared transport clock / playhead — a master broadcasts song position (scene + bar) on a quantized boundary so followers align; or an Ableton-Link-style beat clock. Receivers apply launches to the shared arming (sessionArmedClipIds / songScene) at the target perfStep, rather than only merging track bodies.

  4. Authority & conflict model — who may launch (master vs per-lane ownership), how a follower's local launches reconcile with the master's, and how per-track overrides interact with ownership (actorMayEditTrack).

  5. UI — indicate "following host" vs "free" per player; show the shared playhead; a follow/independent toggle so a player can break off into their own deck (back to model A behaviour) and rejoin.

Tasks

  • song arrangement block: TPL emit/parse, playback reads it, round-trip tests
  • Hub message types for launch.scene / launch.clip / transport (schema + gateway routing)
  • Master-gated apply of launch/transport messages → shared sessionArmedClipIds / songScene at effectivePerfStep
  • Shared playhead broadcast + follower alignment (quantized boundary or beat clock)
  • Authority model (master launches; ownership-scoped per-track overrides)
  • UI: following/independent state, shared playhead indicator, follow toggle
  • Tests: two-actor sim — host launch propagates to follower at the right perfStep; follower override scoped to owned track; break-off/rejoin

Acceptance criteria

  • Host launches P3 → all following players switch to P3 on the same bar.
  • A player can take a per-track clip (their owned track) without desyncing the rest.
  • A player can toggle "independent" (model A) and rejoin "follow" cleanly.
  • Song arrangement is explicit in TPL and round-trips.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions