Skip to main content
Version: 0.x.x

Reactions during Live Stream - JavaScript

To enhance interaction between viewers and speakers during a livestream, a creative approach is to display viewers' reactions to everyone. This can be achieved by creating a lively atmosphere with flying emojis, similar to the experience seen in livestreams on platforms like Instagram.

This guide explains how to implement this engaging feature using the VideoSDK PubSub mechanism.

Step 1: Creating a button to send reactionโ€‹

To implement this functionality, start by creating a button that sends reactions to all users. When this button is clicked, publish a message with the emoji name to all participants using the VideoSDK PubSub mechanism. Additionally, dispatch browser CustomEvents to notify the local participant about the reaction.

function sendEmoji(emoji) {
// Let the local user see their own emoji
window.dispatchEvent(
new CustomEvent("reaction_added", { detail: { emoji } })
);
}

// Create the ๐ŸŽ‰ button
const confettiBtn = document.createElement("button");
confettiBtn.innerText = "Send ๐ŸŽ‰ Reaction";
confettiBtn.onclick = () => {
sendEmoji("confetti");
liveStream?.pubSub.publish("REACTION", "confetti");
};

// Create the ๐Ÿ‘ button
const clapBtn = document.createElement("button");
clapBtn.innerText = "Send ๐Ÿ‘ Reaction";
clapBtn.onclick = () => {
sendEmoji("clap");
liveStream?.pubSub.publish("REACTION", "clap");
};

// Append to DOM
document.body.appendChild(confettiBtn);
document.body.appendChild(clapBtn);

Step 2: Displaying the Reactions to all the usersโ€‹

FlyingEmojiOverlay.js will be utilized to display the reactions on the screen. Listen to the browser event sent on the button click to show the flying emoji and display all reactions sent by other participants

  • Listening to local event and adding the FlyingEmojiOverlay component to the ILSView;
function FlyingEmojisOverlay() {
const EMOJI_MAP = {
confetti: "๐ŸŽ‰",
clap: "๐Ÿ‘",
};

const overlay = document.createElement("div");
overlay.className = "flying-emojis";
document.body.appendChild(overlay);

function showFlyingEmoji(emojiKey) {
const emoji = EMOJI_MAP[emojiKey];
if (!emoji) return;

const span = document.createElement("span");
span.innerText = emoji;
span.className = "emoji-fly"; // Add some CSS animation
overlay.appendChild(span);

// Remove emoji after animation (2s example)
setTimeout(() => {
overlay.removeChild(span);
}, 2000);
}

function handleSendFlyingEmoji(e) {
const { emoji } = e.detail;
if (emoji) {
showFlyingEmoji(emoji); // Show for local user
liveStream?.pubSub.publish("REACTION", emoji); // Send to others
}
}

window.addEventListener("reaction_added", handleSendFlyingEmoji);

// Also listen for others' emojis
liveStream?.pubSub.subscribe("REACTION", ({ message }) => {
showFlyingEmoji(message);
});

return overlay;
}
  • Next, implement the display and removal of flying emojis using simple CSS animations.
function FlyingEmojisOverlay() {
const EMOJI_MAP = {
confetti: "๐ŸŽ‰",
clap: "๐Ÿ‘",
};

const overlay = document.createElement("div");
overlay.className = "flying-emojis";
document.body.appendChild(overlay);

// ๐Ÿงน Handler to remove flying emoji after animation
function handleRemoveFlyingEmoji(node) {
if (overlay.contains(node)) {
overlay.removeChild(node);
}
}

// โœจ Handler to display emoji with random animations
function handleDisplayFlyingEmoji(emojiKey) {
const emoji = EMOJI_MAP[emojiKey];
if (!emoji) return;

const node = document.createElement("div");
node.appendChild(document.createTextNode(emoji));

// Random animation and style
node.className = Math.random() > 0.5 ? "emoji wiggle-1" : "emoji wiggle-2";
node.style.position = "absolute";
node.style.left = `${Math.random() * 100}%`;
node.style.transform = `rotate(${-30 + Math.random() * 60}deg)`;

overlay.appendChild(node);

// Remove after animation ends
node.addEventListener("animationend", () => {
handleRemoveFlyingEmoji(node);
});
}

// ๐Ÿš€ Local user emoji trigger
function handleSendFlyingEmoji(e) {
const { emoji } = e.detail;
if (emoji) {
handleDisplayFlyingEmoji(emoji);
liveStream?.pubSub.publish("REACTION", emoji);
}
}

// ๐Ÿ‘‚ Setup event listeners
window.addEventListener("reaction_added", handleSendFlyingEmoji);

const unsubscribe = liveStream?.pubSub.subscribe("REACTION", ({ message }) => {
handleDisplayFlyingEmoji(message);
});

// ๐Ÿงน Cleanup on unload
window.addEventListener("beforeunload", () => {
window.removeEventListener("reaction_added", handleSendFlyingEmoji);
if (typeof unsubscribe === "function") unsubscribe();
});

return overlay;
}
  • Add the animations for the emojis in the index.css file.
.flying-emojis {
position: fixed;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
overflow: hidden;
pointer-events: none;
user-select: none;
z-index: 99;
}
.emoji {
position: absolute;
bottom: 0px;
left: 50%;
font-size: 48px;
line-height: 1;
width: 48px;
height: 48px;
}
.emoji.wiggle-1 {
animation: emerge 3s forwards, wiggle-1 1s ease-in-out infinite alternate;
}
.emoji.wiggle-2 {
animation: emerge 3s forwards, wiggle-2 1s ease-in-out infinite alternate;
}
@keyframes emerge {
to {
bottom: 85%;
opacity: 0;
}
}
@keyframes wiggle-1 {
from {
margin-left: -50px;
}
to {
margin-left: 50px;
}
}
@keyframes wiggle-2 {
from {
margin-left: 50px;
}
to {
margin-left: -50px;
}
}
  • Finally add the listener to the PubSub topic so reactions from other participants can also be shown.
function FlyingEmojisOverlay() {
const EMOJI_MAP = {
confetti: "๐ŸŽ‰",
clap: "๐Ÿ‘",
};

const overlay = document.createElement("div");
overlay.className = "flying-emojis";
document.body.appendChild(overlay);

// ๐Ÿงน Handler to remove flying emoji after animation
function handleRemoveFlyingEmoji(node) {
if (overlay.contains(node)) {
overlay.removeChild(node);
}
}

// โœจ Handler to display emoji with random animations
function handleDisplayFlyingEmoji(emojiKey) {
const emoji = EMOJI_MAP[emojiKey];
if (!emoji) return;

const node = document.createElement("div");
node.appendChild(document.createTextNode(emoji));

// Random animation and style
node.className = Math.random() > 0.5 ? "emoji wiggle-1" : "emoji wiggle-2";
node.style.position = "absolute";
node.style.left = `${Math.random() * 100}%`;
node.style.transform = `rotate(${-30 + Math.random() * 60}deg)`;

overlay.appendChild(node);

// Remove after animation ends
node.addEventListener("animationend", () => {
handleRemoveFlyingEmoji(node);
});
}

// ๐Ÿš€ Local user emoji trigger
function handleSendFlyingEmoji(e) {
const { emoji } = e.detail;
if (emoji) {
handleDisplayFlyingEmoji(emoji);
liveStream?.pubSub.publish("REACTION", emoji); // Publish the reaction
}
}

// ๐Ÿ‘‚ Setup event listeners for the local user to trigger emoji reactions
window.addEventListener("reaction_added", handleSendFlyingEmoji);

// ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ Listen for reactions from other participants
const unsubscribe = liveStream?.pubSub.subscribe("REACTION", ({ message }) => {
handleDisplayFlyingEmoji(message); // Display the received emoji from others
});

// ๐Ÿงน Cleanup on unload (to avoid memory leaks)
window.addEventListener("beforeunload", () => {
window.removeEventListener("reaction_added", handleSendFlyingEmoji);
if (typeof unsubscribe === "function") unsubscribe();
});

return overlay;
}

API Referenceโ€‹

The API references for all the methods and events utilized in this guide are provided below.

Got a Question? Ask us on discord