Documentation/BroadcastChannel
classWORKER

BroadcastChannel

js/broadcast_channel.js:9
BroadcastChannel(name)

Named asynchronous messaging channel shared by JSplitter panel and Worker realms.

BroadcastChannel is the recommended API for new many-to-many communication between independent JSplitter panels and Workers. Every open channel with the same name participates in the same process-wide JSplitter namespace. A sender publishes a value with postMessage(), and every other currently open channel with that name receives a MessageEvent. The sending channel itself does not receive its own message.

Unlike Worker.postMessage(), which addresses one specific Worker owned by one panel, BroadcastChannel is intentionally decoupled: the sender does not need to know which panels or Workers are listening.

For new code, prefer BroadcastChannel over window.NotifyOthers().
The legacy window.NotifyOthers() mechanism is synchronous and exposes the same live JavaScript value to other panels. This tightly couples the sender and receivers: changes made to a shared object can be visible across panels, and notification handling can directly affect the sender's execution.
BroadcastChannel avoids these limitations by delivering messages asynchronously and using structured clone, so each recipient receives its own independent copy of the transmitted data.
window.NotifyOthers() remains available for compatibility with existing scripts.

Quick start

Create two JSplitter panels.

In the first panel, create a channel and wait for messages:

const channel = new BroadcastChannel('my-script-state');

channel.onmessage = function (event) {
    console.log('Panel A received:', event.data);
};

Then run this code in the second panel:

const channel = new BroadcastChannel('my-script-state');

channel.postMessage({
    type: 'selection-changed',
    playlist: 3,
    index: 34
});

The other panel receives an independent structured-clone copy of the object. The sending channel object does not receive its own post. If the same realm (panel or worker) creates a second BroadcastChannel with the same name, that second channel is a separate receiver and can receive the message.

Panel and Worker use the same API

BroadcastChannel is also available inside Workers. This makes it useful when a Worker should publish state to panels or to other Workers without routing every message through its parent Worker object.

// PANEL
const channel = new BroadcastChannel('analysis-results');
channel.onmessage = event => {
    console.log('Result:', event.data);
};

const worker = new Worker(`
    const channel = new BroadcastChannel('analysis-results');

    channel.postMessage({
        type: 'ready',
        worker: self.name
    });
`, 'analyser');

Use direct Worker.postMessage() when the message belongs to one known parent/Worker pair. Use BroadcastChannel when the relationship is naturally publish/subscribe or when several panels and/or Workers may participate.

Channel names and scope

The constructor name is converted to a string. Use a stable, sufficiently specific name such as my.package.playback-state to avoid accidental collisions with unrelated scripts. Symbols cannot be converted to a channel name and throw a TypeError.

JSplitter does not have browser origins or storage partitions. All live JSplitter panel and Worker realms inside the same foobar2000 process share one BroadcastChannel namespace. Channels do not cross process boundaries and are not persistent; closing foobar2000 destroys the namespace.

Messages and structured clone

postMessage() uses the same structured-clone infrastructure as Worker messaging. Ordinary supported JavaScript values are copied, and supported JSplitter host wrappers can be reconstructed in the destination realm. See the Host objects in messages section of Worker for the current cloneable host-object list.

No ownership transfer

BroadcastChannel has no transfer-list argument. Its postMessage() accepts the message value only. This is different from direct Worker.postMessage() and Worker-global postMessage(), where a second transfer-list argument can move ownership of selected transferable objects to one specific destination.

A broadcast may have zero, one, or many receivers. There is no single receiver that can take ownership of a transferred resource, so BroadcastChannel always uses clone semantics. If a host object is cloneable, every receiver gets its own reconstructed representation and the sender's source object remains usable. No object is detached by BroadcastChannel.

// BroadcastChannel: clone. The sender keeps a usable bitmap.
const channel = new BroadcastChannel('artwork');
channel.postMessage({ bitmap: bitmap });
// bitmap is still usable here

// Direct Worker messaging: transfer ownership to one Worker.
worker.postMessage({ bitmap: bitmap }, [bitmap]);
// bitmap is detached here after a successful transfer

Use BroadcastChannel when several independent panels and/or Workers should receive cloned data by channel name. Use direct Worker messaging when one known destination should take ownership of a transferable resource. See Transferring ownership in Worker for the transfer model and the current transferable host-object list.

Serialization is performed synchronously by postMessage(). Before delivering the message, JSplitter first serializes it for all current receivers. If serialization fails for any receiver, postMessage() throws and no message is delivered, preventing a partial broadcast.

Serialization is also performed when there are no receivers. This matches the Web API behaviour, where posting a value that cannot be cloned can still throw even if nobody is listening.

JSplitter implementation note:
JSplitter currently performs an independent structured serialization for each receiver because some native host-object snapshots are destination-owned. Consequently, side-effectful getters or Proxy traps in the value being serialized can run once per receiver. Avoid side effects in values passed to postMessage().

Receiving messages

Use onmessage or the inherited addEventListener('message', ...) API. Delivery is asynchronous. Messages posted sequentially by one sender are queued in the same order for a given receiver.

Trusted BroadcastChannel message events are MessageEvent objects. In JSplitter they use:

  • event.data — the reconstructed payload.
  • event.origin — empty string.
  • event.sourcenull.
  • event.ports — empty frozen array.
  • event.direction"broadcast".

If a receiver cannot reconstruct a serialized value, its onmessageerror handler receives a MessageEvent whose data is null, direction is "broadcast", and errorMessage contains a JSplitter diagnostic string.

Lifetime and close()

Call close() when a BroadcastChannel instance is no longer needed. This disconnects only that instance from the named channel: it can no longer send or receive messages, while other instances with the same name continue to work normally. Closing is idempotent. A queued delivery is ignored if the destination instance is closed before it runs.

JSplitter automatically cleans up channels when a panel script is reloaded/unloaded or when a Worker terminates. An open channel with a message or messageerror listener is kept alive while its realm lives, matching the useful lifetime behaviour of the Web API; closing the channel releases that listener root. An unreachable open channel with no message listeners may be garbage-collected and removed automatically.

For the browser API model, see HTML Living Standard: BroadcastChannel.

Parameters

NameTypeDescription
name*

Required channel name. The value is converted to a string; Symbol values throw TypeError.

Throws

TypeError

If no name is supplied, if the constructor is called without new, or if the name cannot be converted to a string.

Example source

propertyWORKERreadonly

name

js/broadcast_channel.js:130
name: string = ''

Channel name after string conversion.

propertyWORKER

onmessage

js/broadcast_channel.js:138
onmessage: ?BroadcastChannelMessageCallback = null

Handler invoked asynchronously for successfully reconstructed broadcast messages. The callback receives a MessageEvent whose data contains the cloned payload and whose direction is "broadcast".

propertyWORKER

onmessageerror

js/broadcast_channel.js:146
onmessageerror: ?BroadcastChannelMessageCallback = null

Handler invoked when this receiver cannot reconstruct an incoming broadcast message. The callback receives a MessageEvent with data === null, direction === "broadcast", and a JSplitter diagnostic in errorMessage.

methodWORKER

close

js/broadcast_channel.js:166
close()

Closes this channel object and releases its native endpoint. Future incoming messages are ignored and postMessage() can no longer be used on it.
Calling close() more than once is safe.

methodWORKER

postMessage

js/broadcast_channel.js:154
postMessage(message)

Serializes and asynchronously broadcasts a value to every other currently open BroadcastChannel with the same name. The sending channel itself is excluded.

There is no transfer-list argument and BroadcastChannel never transfers ownership. Cloneable values are reconstructed for receivers while the sender's source value remains usable. Use direct Worker messaging when transferable ownership semantics are required. Clone errors are reported synchronously and no destination receives the message if serialization fails for any current destination.

Parameters

NameTypeDescription
message*

Value to broadcast. An explicit undefined is valid; omitting the argument is not.

Throws

TypeError

If the message argument is omitted.

Error

If the channel is already closed or the value cannot be structured-cloned.