Build a UGC Live Streaming App with Amazon IVS: Broadcast Real-Time with Multi-Hosts (Lesson 4.3)

Build a UGC Live Streaming App with Amazon IVS: Broadcast Real-Time with Multi-Hosts (Lesson 4.3)

Welcome to Lesson 4.3 in this series where we're looking at building a web based user-generated content live streaming application with Amazon IVS. This entire series is available in video format on the AWS Developers YouTube channel and all of the code related to the sample application used in this series can be viewed on GitHub. Refer to the links at the end of the post for more information.

Todd Sharp
Amazon Employee
Published Dec 15, 2023


In lesson 4 of this course, we're learning about real-time streaming with Amazon IVS. In lesson 4.2, we learned how StreamCat generates stage participant tokens which are required for all participants (both broadcasters and viewers). In this lesson, we'll see how StreamCat broadcasts a real-time stream with multiple hosts up to 10,000 viewers.
💡 Note: Make sure that you're familiar with lesson 3 of this course, which covers topics like retrieving stream credentials, accessing user devices, and low-latency broadcasting.

Generating a Host Participant Token

In lesson 4.2, we saw how StreamCat uses the AWS SDK for JavaScript (v3) to generate a stage participant token and persists it to the database. When a user navigates to their own real-time streaming broadcast, a middleware function retrieves the logged-in user's current token (if one exists):
The getCurrentTokenForStage() function runs a query to find the latest token:
If no token is found, or the current token is expired, a new token is generated and persisted.
The token generation process is slightly different for invited guests, as we'll see in a subsequent lesson. When the host as a valid stage participant token, that token is returned to the view and set into the Alpine.js view model where it will be used to join the stage.

Initializing a Stage

The first step to join a stage is to create a strategy (docs), which is an object that describes the desired state of the stage. The strategy object requires three functions: shouldSubscribeToParticipant(), shouldPublishParticipant(), and stageStreamsToPublish().

Subscribing to Participants\

When a remote participant joins the stage, the SDK queries the host application about the desired subscription state for that participant. The options are NONE, AUDIO_ONLY, and AUDIO_VIDEO. When returning a value for this function, the host application does not need to worry about the publish state, current subscription state, or stage connection state. If AUDIO_VIDEO is returned, the SDK waits until the remote participant is publishing before it subscribes, and it updates the host application by emitting events throughout the process. Because the real-time streams in StreamCat always include full audio and video, we will always return SubscribeType.AUDIO_VIDEO.

Publishing Participants

Once connected to the stage, the SDK queries the host application to see if a particular participant should publish. This is invoked only on local participants that have permission to publish based on the provided token. The value returned from this function can be dynamic, so if you wanted to prevent publish until the host or a user clicked a button to indicate they are ready, you could do so with this function. To keep things simple, StreamCat always returns true.

Publishing Streams

This function is used to determine what audio and video streams should be published. For this function, StreamCat will create instances of LocalStageStream from audio and video streams from the user's microphone and camera (see lesson 3.2 for more on creating streams).

Creating a Stage Instance

Now that we have a strategy, and a stage participant token, we can create an instance of the Stage object.

Displaying and Removing Participants

There are a number of events that are exposed on a Stage. StreamCat uses two of these events to update the view when a participant joins or leaves a real-time stream.

Handling Added Streams

When a stream is added - including the host's streams - StreamCat stores the participant and renders the participant as a standalone video and in a composited view that includes all participant's video. The standalone video is useful to give stream hosts a view of the individual participants, and the composite view is used to broadcast to any non-host viewers as well as a preview of the composite stream for hosts.
Real-time host view
Real-time Host View
The renderParticipant() function handles adding any streams to the solo participant video (excluding audio for the local participant in order to prevent audio echo for the host).
The renderAudioToClient() function handles adding the audio stream to the composite view for broadcasting.
Similarly, the renderVideoToClient() function adds the video stream to the composite view.

Handling Removed Streams

Handling when a stream is removed is just the opposite of what happens when a stream is added. The participant is removed from the composite view, and the solo video is removed from the DOM.

Joining a Stage

Once everything is configured, we can join the stage.

Broadcasting a Stage

By default, a stage is viewable by any participant as soon as the participants have joined the stage. To give broadcasters a bit more control over this, StreamCat stores a boolean property on the user's Stage object to give the broadcaster the ability to prevent viewers from joining until they are ready. When the host is ready, they click a button which updates this flag.
The handler for this endpoint will update the user's stage as appropriate, and viewers will be given access to view the real-time stream depending on that flag. We'll learn more about real-time playback in lesson 5 of this course.


In this lesson, we learned how StreamCat creates a real-time broadcasting experience for multiple-hosts. In subsequent lessons, we'll learn about real-time stream playback, as well as how StreamCat lets hosts invite chat users to stream with them.


Any opinions in this post are those of the individual author and may not reflect the opinions of AWS.