Why you probably don't need multiple Daily call objects

One question we sometimes get from developers is about how to avoid multiple call object instances when building with our client SDK for JavaScript using daily-js.

Daily’s call object is the entry point for interacting with participants, room state, and all other aspects of your Daily-powered video or audio call. With daily-js, you join a Daily room through an instance of the call object. One call object can be associated with one Daily video call session at a time. You can leave a Daily room and join a different one with the same call object, but you can’t be in two rooms at the same time. But sometimes, developers may be building a feature that they think requires a user being present in multiple calls simultaneously. Naturally, they gravitate to instantiating another call object and joining another call that way.

But when you create another call object and run your application, you see the following error in your console:

A console error about duplicate call object instances being detected

What’s a dev to do? You think you need multiple call objects, but Daily says being in two Daily rooms at once is an anti-pattern that you shouldn’t be using.

In this post, I’ll run through a few use cases that developers think warrant multiple call objects and explain why they actually don’t. I’ll explain why 99% of the time, you can probably just use Daily’s track subscriptions.

To start, let’s have a quick rundown of what track subscriptions are.

What are Daily’s track subscriptions?

By default, all participants subscribe to the media tracks of all other Daily participants in a room. However, you can also manage track subscriptions yourself.

By setting the subscribeToTracksAutomatically call object property to false on creation, participants will not automatically receive each other’s media tracks when they join a Daily room.

You can also toggle this boolean setting after creating the call object with the setSubscribeToTracksAutomatically() call object instance method.

Whether subscriptions are on by default or not, you can use the updatePartipant() instance method with the setSubscribedTracks configuration object to subscribe to or unsubscribe from remote users’ tracks. Here’s an example:

callObject.updateParticipant(participantSessionID, {
  setSubscribedTracks: { audio: true, video: true, screenVideo: false },

Now that we’ve gone through the basics of track subscriptions, let’s go through a few examples.

Breakout rooms

Let’s say you want to distribute call participants into smaller groups, where they can hear and see each other but nobody else. The common term for this kind of feature is “breakout rooms”, so it makes sense that your first thought might be to literally break these participants out to separate Daily rooms. So how do you do that without having multiple call objects?

Well, you could actually remove the participant from the main Daily room and have them join a new room. But this means your breakout rooms cannot interact with each other as far as Daily is concerned. Once a client is in a new Daily room, it will know nothing about any other room. It won’t get shared events, won’t be able to send data or media to other participants in another room, and won’t be able to view the other room’s presence. Now, for many use cases this might be just fine. Plus, you can bridge that cross-room gap to an extent with our REST API if you have a server component in your application. But sometimes, you might want tighter coupling between participants in different breakout rooms.

The solution for cases where tighter coupling is needed is track subscriptions.

Implementing video call breakout rooms with track subscriptions

So let’s say you have participants A, B, and C speaking to each other in one Daily room that acts as a lobby. In the lobby, each of these participants is subscribed to all other participants’ media tracks. This can be represented in terms of track subscriptions as follows:

A diagram showing three call participants all subscribed to each other's tracks

The double-arrows above represent a two-way subscription to all media tracks. A subscribes to C’s tracks, C subscribes to A’s tracks, and so on.

But let’s say participants B and C want to hop into a smaller “breakout room”, where they can’t hear or see A and where A can’t hear or see them.

To implement this in your application, you’d unsubscribe participants B and C, who are entering a breakout room, from participant A, who remains in the lobby. You’d also unsubscribe participant A from participants B and C. The resulting subscription diagram would look like this:

A diagram showing three call participants, in which only two are subscribed to each other

Note that above I’m using a generic “Subscribed” and “Unsubscribed” state to show the most basic possible variant of track subscriptions: you can either see and hear someone or you can’t. In practice, Daily allows even more granular control. For example, you could subscribe to someone’s video, but not audio.

Practical example

I’ve previously built an example of this kind of feature in a spatialization setting, where users can traverse a larger world and then optionally sit at a table to interact with a smaller group:

A call participant entering a "breakout room" in the form of a meeting table

Check out the Daily integration in the spatialization demo repository if you’d like to dig into the code.


Following up on the breakout room solution above, let’s say you have one “admin” participant who wants to be able to speak to everyone across all rooms. You might wonder if the best approach is to have everyone stay in a specific “broadcast” room alongside their breakout session, where this admin user can unmute themselves to talk.

In reality, this can be done with track subscriptions as well by having all breakout room participants subscribe to the broadcasting user.

Let’s take the same three-participant room example we discussed above, with participants A, B, and C. Participants B and C are in their own breakout “room”. They cannot see or hear A, and A cannot see or hear them.

But wait! Participant A is actually an admin user who has the power to broadcast their video and audio to everyone in the room, including participants who are in separate breakout sessions.

There’s probably a button of some sort they’ll press in your application UI to do so. That action will then signal to all call participants that participant A wants to make an announcement. At this point, participant clients can call updateParticipants() with the setSubscribedTracks property to subscribe to participant A’s media tracks.

Note that participant A does not need to see and hear everyone as part of sending a broadcast, so these will be one way subscriptions. Participants B and C will subscribe to A, but A will not subscribe to them:

A diagram showing three call participants, in which one participant has a one-way subscription active

Practical example

In my aforementioned spatialization demo, when a user steps onto a specified broadcast spot, I have all other call participants subscribe to their video and audio. You could do the same thing in a non-spatialization context whenever the admin user presses a “Broadcast” button, for example.

A screenshot of a "broadcast" spot in which a participant can send their media tracks to all other participants

Check out the subscription code in the demo repository.

Observing other participants in the background

Let’s say you have a game; kind of like our Code of Daily: Modern Wordfare social game, maybe. You want to have non-playing observers in the game. Maybe the observers can see the full state of the game board and talk to each other, but not to the users playing the actual game.

You might think that the observers need to join two Daily rooms: one where they can see and hear all players, and another where they can talk amongst themselves.

In reality, track subscriptions can facilitate this feature as follows:

  • Each observer subscribes to every other observer’s tracks.
  • Each observer also subscribes to every player’s tracks.
  • Each player subscribes to every other player, but not to any of the observers.

Don’t worry, I’m not going to beat you over the head with more diagrams; if you have any questions, just reach out and we can walk through your use case.

Special considerations

There are a couple of special use cases that may require extra consideration when it comes to using track subscriptions.

Calls with over 1,000 mics and cams on simultaneously

We’re constantly expanding our call limits. Currently, Daily supports real-time calls with up to 1,000 microphones and cameras on. If your application will have more than 1,000 people speaking or sharing their camera at the same time, you’ll want to consider that in your implementation.

For such large use cases, you’re likely already going to have some sort of server component in your app. Instead of joining different Daily rooms at the same time to stay within the limit, there’s another potential approach: Sending meeting-wide communications to individual participants through your dedicated server (using WebSockets to push data to the clients, for example) and having users connect to a single room within their smaller group. You can read more about using WebSockets in conjunction with Daily functionality in my post about using WebSockets in my Daily-powered social game.

Calls with sensitive security requirements

Technically, any client could subscribe to any other client’s media tracks if they wanted to, and if the end user was savvy enough to do so. Daily currently does not gate track subscriptions on the server side (although this is something we have plans for in the future). This means if your track subscription use case involves more sensitive data, you should probably architect your application to use multiple private rooms that can only be entered with a valid meeting token. Then, you can share central state through a server component, similarly to what I described above.


As you can see, most use cases don’t necessitate multiple call objects. But developers building with Daily never fail to surprise us in the unique projects and ideas they come up with, and your case could very well need some special handling! So if you’re not sure how to reason through this topic with your specific feature or if you have any other questions, please don’t hesitate to reach out to our awesome support team or post in our WebRTC community, peerConnection.

Never miss a story

Get the latest direct to your inbox.