Interactive Live Stream Tutorial with Red5
Live streaming has long been treated as a one-way experience: press play, sit back, and watch.
But that's no longer enough.
Today, users expect more than just video; they want to interact, react, and participate in real-time. Whether it's live sports, auctions, gaming events, or virtual shows, the most memorable interactive experiences aren't just watched, they are shared, often through a watch party.
This is where the next layer of live streaming comes in.
Moments like:
A wave of emoji reactions as a winning bid lands in a live auction
Real-time chat is lighting up alongside a product drop or concert stream
Interactive overlays that respond instantly to what's happening: live polls closing, prices updating, bids climbing
These experiences build on top of a great video; they don't replace it.
The pattern applies across industries:
Media & Entertainment: Multi-camera sports, fan polls, real-time overlays
Live Shopping & Social Commerce: Flash-sale countdowns, shoppable overlays, instant reactions
Online Auctions & Bidding: Synchronized audio/video with real-time lot updates and bid confirmations
Education & Future of Work: Interactive classrooms and collaborative suites.
Gaming: Ultra-low latency streams for interactive gameplay and real-time odds
Telehealth: HIPAA-compliant consults with secure device telemetry
Social: Scalable fan communities with fine-grained access control
Online Auctions & Bidding: Live bidding with synchronized audio and video, real-time lot updates, and instant bid confirmations.
In this tutorial, we'll show you how to extend a powerful live streaming foundation by combining:
Red5 for ultra-low latency live video delivery PubNub for real-time messaging, synchronization and interactivity with interactive SDKs
Together, these technologies enable developers to create fully interactive live event applications where video and real-time engagement work seamlessly side by side, including on Android devices.
With Red5 handling reliable, real-time video delivery, developers already have a strong foundation for live applications.
By introducing real-time messaging and an event-driven system, you can build on top of that foundation to:
Connect audiences in real-time
Synchronize interactions across viewers
Create dynamic, responsive live and on-demand experiences
This combination transforms a live stream into something more powerful: A shared experience.
Want to skip ahead? Jump straight to the tutorial to start building, or read on for the architecture overview first.
What We're Building
We will build and showcase how we set up a live soccer production demo with video streaming, real-time chat overlays, emoji reactions, live commentary, shoppable product cards, and in-play betting. This project serves as an excellent use case for integrating multiple technologies to create a seamless user experience.
The end result feels like a modern live sports app where every feature responds in real-time, not because we built a complex backend to orchestrate it all, but because we chose the right tools for each layer, including in-app features that enhance user engagement.
| Layer | Technology |
|---|---|
| Frontend | Next.js 16 |
| Real-time Messaging | PubNub JavaScript SDK |
| Video Delivery | Red5 Cloud |
Red5 Cloud ingests streams via RTMP or WHIP and delivers to viewers over WHEP with sub-second latency.
PubNub carries everything else: chat, reactions, commentary, products, bets, video sync, WebRTC signalling, and server control. Each concern gets its own channel.
Note: This demo does not require a backend for production use, but for demo purposes, we have used scripted events sorted into a timeline to simulate how a live stream would function.
PubNub Channel Architecture
We have sorted each PubNub Channel into a different feature using PubNub's APIs. This is so that components only need to subscribe to what they need. For example, the betting card doesn't need to re-render when a chat message arrives. By utilizing the PubNub Chat SDK, developers can efficiently manage these channels to streamline their workflows.
export const chatChannelId = "game.chat"
export const streamReactionsChannelId = "game.stream-reactions"
export const commentaryChannelId = "game.commentary"
export const betsChannelId = "game.bets"
export const betResultsChannelId = "game.bet-results"
export const productHighlightChannelId = "game.product-highlight"
export const videoControlChannelId = "game.video-control"
export const serverControlChannelId = "game.server-control"
| Channel | Purpose |
|---|---|
| game.chat | User messages and scripted chat |
| game.stream-reactions | Emoji broadcasts + presence/occupancy |
| game.commentary | Timestamped match commentary using TrueTime |
| game.bets | Bet proposals and placed bets |
| game.bet-results | Win/loss resolution |
| game.product-highlight | Flash sale product cards |
| game.video-control | Playback sync (STATUS, SEEK) |
| game.server-control | START, STOP, TOGGLE_CHAT |
| game.stream-status | PiP stream lifecycle (heartbeats) |
| game.stream-signal | WebRTC signaling (SDP/ICE) |
How PubNub and Red5 Work Together
In this demo, the option to start your camera is powered by Red5 Pro for the video stream itself. This integration is crucial for delivering high-quality real-time streaming experiences. However, PubNub manages the coordination through the game.stream-signal and game.stream-status channels. When a viewer decides to go live, PubNub broadcasts a notification to every other viewer watching the stream, letting them know someone has turned on their camera. This enables real-time fan reactions to the game being shown — similar to the "go live" functionality seen on platforms like TikTok Live or Instagram Live during live events.
Setting up the PubNub Keyset
To set up a PubNub keyset, we will need to go to the admin portal and create an application.
For our keyset, we need to enable the following features in the PubNub Admin Portal:
Pub/Sub (enabled by default)
Presence — for live viewer counts
App Context — for storing user profiles, wallet balances, and cart data
Message Persistence — for chat history
Once those are toggled on, copy the Publish Key, Subscribe Key, and Secret Key into both the backend/.env and web/.env files.
After the keyset is created, we can copy and paste the publish and subscribe key to the .env in the /web folder.
NEXT_PUBLIC_PUBNUB_PUBLISH_KEY=PUBNUB_PUBLISH_KEY
NEXT_PUBLIC_PUBNUB_SUBSCRIBE_KEY=PUBNUB_SUBSCRIBE_KEY
Setting up the Red5 Cloud Keyset
To set up Red5 Cloud, a scalable video streaming solution, sign in at red5.net and navigate to Node Groups. Create a new Node Group or use an existing one. This gives you a dedicated streaming infrastructure in your chosen region, similar to Amazon's cloud services. Red5 Cloud operates as a Platform as a Service (PaaS), providing flexibility and scalability for your streaming needs.
From the Node Groups page, note your:
Host - your instance URL (e.g. your-instance.cloud.red5.net)
Node Group ID - shown in the Node Group ID column (e.g. Baselin-41fA)
The app name defaults to live, which matches the standard Red5 configuration. These values are used to construct the WHIP and WHEP endpoints that the client SDK connects to:
https://{host}/as/v1/proxy/whip/live/{streamName}?nodeGroup={nodeGroupId}
https://{host}/as/v1/proxy/whep/live/{streamName}?nodeGroup={nodeGroupId}
Add these to web/.env:
NEXT_PUBLIC_RED5_HOST=your-instance.cloud.red5.net
NEXT_PUBLIC_RED5_NODE_GROUP=your-node-group-id
NEXT_PUBLIC_RED5_APP=live
NEXT_PUBLIC_TURN_SERVER_URL=stun:stun2.l.google.com:19302
With both keysets ready, your env files should look like this:
backend/.env
PUBNUB_PUBLISH_KEY=pub-c-xxxxx
PUBNUB_SUBSCRIBE_KEY=sub-c-xxxxx
PUBNUB_SECRET_KEY=sec-c-xxxxx
GUIDED_DEMO=true
PORT=3002
web/.env
NEXT_PUBLIC_PUBNUB_PUBLISH_KEY=pub-c-xxxxx
NEXT_PUBLIC_PUBNUB_SUBSCRIBE_KEY=sub-c-xxxxx
NEXT_PUBLIC_RED5_HOST=your-instance.cloud.red5.net
NEXT_PUBLIC_RED5_NODE_GROUP=your-node-group-id
NEXT_PUBLIC_RED5_APP=live
NEXT_PUBLIC_TURN_SERVER_URL=stun:stun2.l.google.com:19302
Running the Repo
Running the repo, you want to run this command in the root of the repository ./start.sh. Make sure you have cloned the repository from GitHub to ensure you have the latest version.
This script installs dependencies (if needed), then starts two processes:
Backend on port 3002 — the timeline engine that orchestrates scripted events
Frontend on port 3000 — the Next.js app with the full interactive experience
Open http://localhost:3000, pick a user avatar, and click Start Demo to launch the simulation. The backend will begin publishing scripted events — chat messages, commentary, reactions, product highlights, and bets — all synchronized to a pre-recorded video timeline.
Setting Up the Red5 Video Pipeline
With accounts and keys in place, let's look at how each technology is wired into the application, starting with the video layer, which is crucial for enhancing the user experience. The integration of data streaming capabilities ensures seamless video delivery and real-time interaction.
Red5 Cloud uses WHIP (WebRTC-HTTP Ingestion Protocol) for publishing and WHEP (WebRTC-HTTP Egress Protocol) for subscribing. These are standardized protocols, which means the client SDK talks to Red5 over standard HTTP and WebRTC - no proprietary transport. This setup is essential for efficient data streaming, allowing for robust and scalable video communication.
All Red5 configuration lives in a single function that reads from environment variables:
export const getRed5StreamConfig = (streamName: string, isMobile: boolean) => {
const host = process.env.NEXT_PUBLIC_RED5_HOST || ""
const nodeGroup = process.env.NEXT_PUBLIC_RED5_NODE_GROUP || ""
const app = process.env.NEXT_PUBLIC_RED5_APP || "live"
return {
whipEndpoint: `https://${host}/as/v1/proxy/whip/${app}/${streamName}?nodeGroup=${nodeGroup}`,
whepEndpoint: `https://${host}/as/v1/proxy/whep/${app}/${streamName}?nodeGroup=${nodeGroup}`,
iceServers: [{ urls: process.env.NEXT_PUBLIC_TURN_SERVER_URL }],
bandwidth: { audio: 56, video: 2000 },
mediaConstraints: {
audio: true,
video: isMobile
? { width: { ideal: 720 }, height: { ideal: 1280 } }
: { width: { ideal: 1280 }, height: { ideal: 720 } },
},
}
}
Publishing a Stream
The useRed5Publisher hook wraps the Red5 Cloud SDK's WHIPClient. Publishing a stream is a three-step process: initialize the client with the WHIP endpoint, configure media constraints, and call publish() to ingest the stream. It's important to ensure that all necessary metadata is correctly set up for the stream to function optimally:
const red5prosdk = await import("red5pro-webrtc-sdk")
const publisher = new red5prosdk.WHIPClient()
await publisher.init({
endpoint: config.whipEndpoint,
host: config.host,
app: config.app,
streamName,
rtcConfiguration: { iceServers: config.iceServers },
mediaConstraints: config.mediaConstraints,
bandwidth: config.bandwidth,
})
await publisher.publish(streamName)
The hook also supports toggling mic and camera via replaceTrack() — essential for mobile IoT experiences.
Subscribing to a Stream
On the viewer side, useRed5Subscriber creates a WHEPClient and connects to the same stream, ensuring that notifications are enabled for any updates or changes in the stream status:
const subscriber = new red5prosdk.WHEPClient()
await subscriber.init({
endpoint: config.whepEndpoint,
host: config.host,
app: config.app,
streamName,
rtcConfiguration: { iceServers: config.iceServers },
})
await subscriber.subscribe()
The hook includes retry logic up to 5 attempts with a 3-second delay. This handles the common case where a viewer opens the page before the publisher has started broadcasting.
Ingesting Pre-Recorded Video via RTMP
For the demo, you can stream a pre-recorded match video into Red5 using FFmpeg:
ffmpeg -re -stream_loop -1 \
-i "$VIDEO_FILE" \
-c:v libx264 -b:v 2000k \
-preset veryfast -tune zerolatency \
-c:a aac -b:a 128k \
-f flv "rtmp://${RED5_HOST}:1935/live/${STREAM_NAME}"
Red5 ingests this RTMP stream and re-delivers it over WHEP to all connected viewers. The important thing is that from the viewer's perspective, there's no difference between a live camera and a pre-recorded stream. Red5 delivers both with the same sub-second latency.
At this point, we have a working live video pipeline. Viewers can watch a low-latency stream. But the stream is still passive — there's nothing to interact with. That's where PubNub comes in.
Connecting the Two Pipelines: How Red5 and PubNub Work Together
Red5 is great at what it does — delivering video with ultra-low latency. But a live stream needs more than just video delivery. Someone needs to answer questions like:
Who's live? When a broadcaster starts broadcasting their camera, how do the other 10,000 viewers find out?
Who's watching? How do you show a live viewer count alongside the Red5 stream?
How do peers connect? If a viewer wants to share their camera with others via WebRTC, who handles the signalling?
Red5 doesn't solve these problems, and it shouldn't. These are coordination and messaging problems, not video delivery problems. PubNub handles all of them with optimization in mind.
Stream Discovery: Knowing Who's Live
When a viewer decides to "Go Live" with their camera, PubNub is the streaming solution that notifies every other viewer. The broadcaster publishes a STREAM_STARTED message on the game.stream-status channel, and every connected client receives it instantly:
pubnub.publish({
channel: "game.stream-status",
message: {
type: "STREAM_STARTED",
userId,
timeRemaining: 60,
},
})
This is the discovery layer that Red5 doesn't provide. Red5 can deliver the video once a connection is established, but it's PubNub that tells everyone "hey, there's a stream to watch." The broadcaster also publishes a STREAM_HEARTBEAT every 5 seconds. If heartbeats stop arriving, a watchdog timer on each client cleans up the stale stream. When the broadcast ends, a STREAM_STOPPED message tells everyone to tear down their connections.
All of this lifecycle management, crucial for social media platforms, starts, heartbeat, and stop flows through PubNub Pub/Sub. Red5 never needs to know it's happening.
What's Next
This demo is a starting point. The two-pipeline architecture is designed to grow. Here are features you could add without rethinking the foundation:
Live polls — add a game.polls channel, publish poll questions, collect votes via Pub/Sub
Tipping — add a game.tips channel, deduct from the wallet, animate coins flowing to the streamer
Highlight clips — use PubNub to coordinate clip creation timestamps with Red5's recording capabilities
Moderation — leverage the Chat SDK's built-in moderation for profanity filtering and user muting
The pattern is always the same: Red5 handles video. PubNub handles data. The frontend weaves them together. Define a channel, define your message types, subscribe where you need the data, and publish when you have something to say.
Getting Started
Clone the repository:
git clone <repo-url>
cd live-shopping-red5
Create a PubNub app at admin.pubnub.com, enable Presence, App Context, and Message Persistence. Add keys to both backend/.env and web/.env.
Set up Red5 Cloud at https://www.red5.net, create a Node Group and add your host and node group ID to web/.env.
Start:
./start.sh
Open http://localhost:3000, pick a user, and click Start Demo.