Changelog
Changelog
v4.0.0 (2026-10-24)
A rewrite in strict TypeScript with no dependencies. See the migration guide for the before and after of every call.
Added
BroadcastChannelSignaling: signaling between tabs of the same origin, with no server.peerData.close()leaves every room and stops listening to signaling.room.participants, the other peers in the room by id,participant.connection, itsRTCPeerConnection, andparticipant.channel, its data channel.- The
connectTimeoutoption: a participant whose data channel doesn’t open in time, 30 seconds by default, is closed. SocketChannelannounces this peer again in every room it’s in when the socket reconnects.offon rooms and participants, and typed event names and values foronandoff.- A participant
connectedevent, fired when its data channel opens. - Runnable examples in
examples/: a chat between tabs, a video call, a chat through a socket.io 4 signaling server, and sending more than a data channel buffers. The signaling server runs on Vite’s dev and preview servers through a Vite plugin, which the new Vite guide shows.
Changed
PeerDatais a named export, and takes the signaling and an options object:new PeerData(signaling, { configuration, channel }).SocketChanneltakes a socket.io client socket you create, of the version that matches your server.- A custom signaling channel implements
sendandsubscribeinstead of listening on anEventDispatcher. - Getters became properties:
room.id,participant.id. - Connections negotiate with the perfect negotiation pattern: either side can add media at any time, and offers that cross are resolved.
- Each connection has one data channel that both sides create, so it exists from the start and renegotiation never replaces it. v3 and v4 peers can’t exchange data in the same room.
- Data sent before the channel opens waits, copied, and goes out in order once it does.
sendon a disconnecting or disconnected participant throws, andsendrefuses aBlob, which could arrive out of order.room.sendsends to every participant it can before throwing for one it can’t.- Binary data arrives as an
ArrayBufferin every browser. disconnectedfires once per participant, and only for a closed or failed connection; a disconnected participant leavesroom.participants.- Negotiation errors go to the participant’s
errorevent, and errors with no listener are reported as uncaught. - A CONNECT from an id that still has a participant replaces the participant.
connectthrows for an empty room id and afterclose.RoomandParticipantare exported as types only,SignalingEventTypeis an object and a union type instead of an enum, androom.disconnect()andparticipant.close()return nothing.- ES module only, with types; no CommonJS or UMD build.
Fixed
- Peers that shared no media never connected, so data between them never arrived.
- Data sent from the
participantlistener, before the channel opened, was dropped. - Peers that joined with a stream at the same moment never connected: their offers crossed, and neither side recovered.
Removed
EventDispatcher,participant.renegotiate(),onSignalingEvent, and thegetEventDispatcher,getConfiguration,getParticipantIdandgetStreamgetters.SignalingEventType.ERROR, and theipaddrandlogsocket events.- The socket.io-client and uuid dependencies, and the webrtc-adapter peer dependency, which v3 never imported.