Files
2026-05-27 23:29:53 +01:00

6.1 KiB

OpenSpec Change: enterprise-react-audio-stream

  • Change ID: enterprise-react-audio-stream
  • Package: @suaveplan/react-audio-stream
  • Category: react
  • Target path: packages/ui/react/react-audio-stream
  • Status: PROPOSED
  • Date: 2026-05-15
  • Design: ./design.md
  • Spec delta: ./specs/react-audio-stream/spec.md

Replaces the prior 2026-04-24 draft flagged NEEDS-REWRITE. See ../REACT_AUDIO_WAVE_REWRITE_COMPLETE.md.


Summary

Deliver @suaveplan/react-audio-stream on genesis from scratch at enterprise quality. The package provides WebRTC-based real-time audio streaming for the React audio surface. It is transport-agnostic at its core (a StreamSession abstraction with peer fan-out) and ships two transport implementations: WebRTC (default) and WebSocket (server-relay fallback). Sessions are integrated with the unified architecture: a remote peer's incoming audio is materialised as an AudioPlayerPlugin-shaped source on a dedicated chain (so it benefits from FX, sidechain ducking, analyser readouts), and the local outgoing audio is captured via the engine's tap-to-bus surface from any track's post-FX point.

The package exposes:

  1. <AudioStreamProvider> — owns one StreamSession instance per provider, nested under <AudioPlayerProvider> (or any <AudioProvider>); accepts an injectable signalling adapter (default: WebSocket signalling).
  2. Hooks: useAudioStream(participantId), usePeerStreams(), useStreamControls(), useStreamReconnect(), useStreamMixer(), useStreamInterruption().
  3. Components: <RemotePeerStrip>, <StreamMixer> (per-peer level/mute/solo + transport-share toggle), <StreamHealthIndicator> (latency, packet loss, codec), <StreamReconnectToast>.
  4. Auto-reconnect with exponential back-off + interruption classification (network vs media-permission vs server-disconnect).
  5. Transport-share mode: a designated peer's transport play/pause/seek events drive every connected peer's local transport so collaborative listening stays in sync.
  6. OTel instrumentation (active sessions gauge, peer-connect / disconnect counters, ICE-failure counter, RTT histogram).

The package owns no DSP, no audio context, no codec implementation — every audio surface goes through @suaveplan/react-audio hooks; WebRTC peer streams become first-class source plugins on dedicated chains.


Current-state audit (genesis)

The package does not exist on genesis yet — fresh build replacing the obsolete pre-tier 2026-04-24 draft. The upstream react/media/react-audio-stream/ is layered on the monolithic @suaveplan/react-audio plugin surface; this rewrite re-derives every behaviour against the unified architecture.


Parity Checklist (vs upstream)

Capability Upstream Migrated Notes
ReactAudioStream plugin class ✅ ⬆️ Replaced by the StreamSession abstraction + per-peer AudioPlayerPlugin-shaped sources.
Session management ✅ ✅ Now formal lifecycle: connect/join/leave/disconnect with FSM.
Peer stream fan-out ✅ ✅ Per-peer MediaStream → MediaStreamTrack → engine source plugin.
Per-stream mixing controls ✅ ⬆️ Each peer gets a track + chain in the engine; user controls = <StreamMixer> over useStreamMixer.
Effect routing ✅ ⬆️ Per-peer chains support any FX from audio-fx-plugin-base.
Transport-control sharing ✅ ⬆️ Single designated peer drives transport; explicit transfer-of-control.
Auto-reconnect ✅ ✅ Exponential back-off with cap; user-overridable.
Interruption classification ✅ ✅ Network / permission / signalling / peer / unknown buckets.
WebSocket transport partial ✅ First-class equal-peer transport.
Storybook coverage ⚠ ✅ Every component default + interactive + edge.
Coverage ~82 % ✅ 100/100/100/100.

No 🚫 rows.


Available workspace deps on genesis

Package Role
@suaveplan/react-audio (workspace:^) Provider + hooks
@suaveplan/audio-fx-plugin-base (workspace:^) PluginChain, source plugin shape
@suaveplan/audio-transport (workspace:^) Transport for share mode
@suaveplan/network-transport-websockets (workspace:^) Default WebSocket transport
@suaveplan/network-discovery-trystero (workspace:^) Optional Trystero (BitTorrent-tracker WebRTC signalling) transport
@suaveplan/fsm (workspace:^) Session lifecycle state machine
@suaveplan/retry (workspace:^) Exponential back-off for reconnect
@suaveplan/error, @suaveplan/types, @suaveplan/opentelemetry, @suaveplan/events foundations
react (peer), zustand, zod

File layout

packages/ui/react/react-audio-stream/
  src/
    index.ts
    error-codes.ts
    provider/
      audio-stream-provider/
      provider-options/
    session/
      stream-session/
      session-fsm/
      peer-channel/
      stream-source-plugin/
      interruption-classifier/
    transports/
      webrtc/
      websocket/
      trystero/                       ← optional subpath
    hooks/
      use-audio-stream/
      use-peer-streams/
      use-stream-controls/
      use-stream-reconnect/
      use-stream-mixer/
      use-stream-interruption/
      use-transport-share/
    components/
      remote-peer-strip/
      stream-mixer/
      stream-health-indicator/
      stream-reconnect-toast/
    __benchmarks__/
      session-fanout.bench.ts
  tests/
    integration.test.tsx
    purity.test.ts
    exports.test.ts
  e2e/
    two-peer-sync.spec.ts

Impact

Breaking changes: none — fresh build.

Prerequisites: react-audio, audio-fx-plugin-base, audio-transport, network-transport-websockets, fsm, retry, foundations.

Affected dependents

  • @suaveplan/react-audio-player — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: <PremiumPlayer>'s multiPeer option consumes this package's use-multi-peer-sync hook through an optional subpath; created against this spec)

No other dependents (verified by bunx turbo ls --filter=...@suaveplan/react-audio-stream).