You know those digital menu boards that you see at quick service restaurants? Well, last year I got to work on one of those.
Ok, but what does that have to do with the Broadcast Channel API?
A little bit of background
In our particular case, the menu board is a React web app running in a Chromebox at the restaurant. It supports two displays and we control which page of the menu it shows based on the URL route.
One of the requirements was that we needed to animate certain elements in the menu, but there was a catch. The animations had to be synchronized across the displays. We couldn't just fire up the animation loop whenever we wanted. We needed a way for the two pages to communicate with each other to start their timers at the same time.
Enter the Broadcast Channel API.
What is the Broadcast Channel API?
It is a Web API that allows basic communication between browsing contexts (i.e. windows, tabs, iframes, webviews) on the same origin.
You start by creating a BroadcastChannel
object and giving it a name. If a channel with the same name already exists, it will join the channel instead.
const channel = new BroadcastChannel("channel_name")
To send a message you call the postMessage()
method on the created object, which takes any object as an argument.
channel.postMessage("is this thing on?")
This will dispatch a message
event to each of the contexts that have joined the channel. We can then run a function for this event with the onmessage
event handler.
channel.onmessage = ev => {
console.log(ev.data)
}
// is this thing on?
To disconnect from the channel you call the close()
method on the object.
channel.close()
Back to our app
We were able to leverage this API to communicate back and forth between the two displays and make sure they start their animation loop exactly at the same time. Keep in mind that each display "boots up" independently at slightly different times, so we need some negotiation up front to know when both of them are ready.
The basic logic looks something like this:
/**
* In this example I will refer to the first display to finish
* booting up as "display 1". The second one to be ready will
* be "display 2".
*/
// display 1 creates the channel; display 2 joins it
const syncChannel = new BroadcastChannel("animation-sync")
syncChannel.onmessage = ({ data }) => {
switch (data.type) {
// display 1 receives the "READY" message from display 2
// and posts a message to start setting things up
case "READY": {
syncChannel.postMessage({ type: "START" })
break
}
// display 2 receives the "START" message
case "START": {
// record the current time (just to be safe we pad it 5s
// to allow display 1 to receive the next message)
const syncTime = new Date().getTime() + 5000
// outside function that schedules the animations
scheduleAnimations(syncTime)
// send "SYNC" message to display 1 with the syncTime
syncChannel.postMessage({ type: "SYNC", syncTime })
break
}
// display 1 receives the "SYNC" message with the syncTime
case "SYNC": {
scheduleAnimations(data.syncTime)
break
}
default:
break
}
}
// display 2 sends the "READY" message to display 1
animationSyncChannel.postMessage({ type: "READY" })
Play with it
I made a simplified demo of this in Codesandbox. And here's a short preview of it:
Pretty cool, right?
Caveat
Not every browser supports this natively. Fortunately, there's a npm package called broadcast-channel that has the same API and uses the native implementation in browsers that support it. Plus, it allows you to use it in Node too!
Cover photo by Prince Abid
Top comments (4)
This is awesome! I might try to make a game that goes across multiple screens ๐
Thanks for the clear explanation!
Glad to hear it was useful! That sounds like a great idea, I'd love to see it if you do ๐
Nice one! I'm using this API for our Single Sign-On implementation.
Thanks! And yes, lots of cool use cases for it ๐