Home›Journal›This post

BroadcastChannel vs SharedWorker for Multi-Tab State

Replay one tab lifecycle through versioned BroadcastChannel peers and a volatile SharedWorker owner, then compare both with an independent revision reducer.

JP
JP Casabianca
AI Engineer and Product Designer · full-stack delivery · Bogotá

BroadcastChannel vs SharedWorker decides whether browser tabs exchange peer notifications or send commands to one volatile coordinator. This comparison replays the same join, edit, delay, reload, duplicate, close, and restart trace through both designs so ownership failures become visible before production.

BroadcastChannel vs SharedWorker starts with ownership

BroadcastChannel vs SharedWorker is a choice between peer notification and one volatile coordinator. If every tab can own state and reconcile versions, a channel is the smaller primitive. If tabs need one in-browser authority for ordering and shared resources, a SharedWorker can own that role while it remains alive. Good cross-tab communication begins by naming that ownership.

This comparison replays one deterministic join, edit, delay, reload, duplicate, close, and restart trace through both designs. An independent reducer computes expected revisions and outcomes. The goal is not a throughput podium; it is to expose who may commit state, who repairs gaps, and what disappears when a context dies.

The first architectural question is therefore “notifications or an owner?” A BroadcastChannel has no built-in canonical record. Messages go to other eligible channel objects, and the sender should not assume it receives its own post. A SharedWorker can keep a canonical record and serialize MessagePort commands, but that record is volatile and can vanish with the worker.

This boundary is different from Atomics.waitAsync worker coordination, which concerns waiting on shared memory among worker agents. Multi-tab state usually needs message identity, revisions, lifecycle repair, and often durable storage rather than a shared-memory wait primitive.

Peer mesh and central owner solve different problemsThree peer tabs exchange versioned events on the left while three MessagePorts converge on one SharedWorker coordinator on the right.BROADCAST PEERSSHARED COORDINATORTAB ATAB BTAB CTAB ATAB BTAB CSHAREDWORKERvolatile revision ownernotify · reconcile · peer-local statecommand · serialize · broadcast commit
Peer mesh and central owner solve different problems. The smallest correct choice follows the ownership model, not the number of arrows.
  • BroadcastChannel connects eligible peers but owns no canonical record.
  • Each peer applies local commands and reconciles version gaps.
  • SharedWorker receives commands through MessagePorts and owns one volatile revision.
  • Neither topology is durable after all relevant browser contexts disappear.

Reading rule: labels, values, shapes, order, and line styles carry the conclusion; color is supplementary.

Model origin, partition, and channel identity

Both APIs require precise scope. BroadcastChannel communication is limited by origin and storage partition rules; a matching string alone does not make unrelated contexts peers. SharedWorker construction is also origin-scoped, and name plus script URL help determine the worker identity. Product documentation should not compress those rules into “all tabs.” This is the first constraint in a BroadcastChannel vs SharedWorker decision.

Represent each virtual context with origin, top-level partition, application version, channel or worker identity, tab ID, and lifecycle generation. The simulator accepts only an explicit bounded set. A partition-confusion mutant removes one identity component so a forbidden context appears to join; the oracle rejects that trace.

Lifecycle identity matters after reload. Reusing tab ID without a new generation can make delayed messages from the old document look current. Pair a stable logical client ID, when needed, with a fresh session generation. Message IDs then combine sender, generation, and monotonic sequence, making duplicate detection deterministic.

Neither primitive makes an authorization boundary. Same-origin scripts may still have different product privileges, and compromised code can send well-formed messages. Validate command shape and application policy before applying state. Authorization and role allowlists are deliberately outside the bounded lab: it rejects a mismatched storage partition, but it does not model product privileges or include an unauthorized-edit mutant.

Build the BroadcastChannel peer protocol

A robust peer protocol carries a snapshot version, message ID, base revision, proposed value, and sender generation. On join, a tab asks for the latest snapshot. Peers respond, and the joiner selects the highest valid revision under a deterministic tie policy. Edits are accepted only when base revision matches local state; otherwise the tab requests reconciliation. That explicit protocol is what turns the Broadcast Channel API into reliable tab coordination.

BroadcastChannel behaves like a bus, not replicated-state magic. Two tabs can edit revision 4 concurrently and each propose revision 5. The application must choose a conflict rule, route writes through durable storage, or accept domain-specific convergence. This article uses a deterministic winner only to make the trace inspectable, not as a universal collaboration algorithm.

The sender-self-delivery mutant exposes a subtle bug: code posts an edit and waits for its own message before updating local state. The native model routes to other channel objects, so the sender can stall. The correct adapter applies its local command once, records its message ID, then broadcasts the resulting event for peers.

Stale and duplicate events need separate outcomes. A stale revision should trigger reconciliation in a production peer protocol; a duplicate message ID should be ignored without advancing state. Treating both as successful edits corrupts audit trails. The bounded lab’s table records one outcome string per action and its aggregate counters. It does not export a peer-local before/after ledger or a full reconciliation event stream.

Build the SharedWorker coordinator protocol

The SharedWorker design moves canonical volatile state into one worker. Each tab opens a MessagePort, sends a hello with identity and last known revision, then receives a snapshot. Commands include client ID, generation, command ID, and base revision. The worker validates, serializes, increments the revision once, and broadcasts the committed event to connected ports. In BroadcastChannel vs SharedWorker, this move from messages to commands is the key architectural change.

Some MessagePort patterns require an explicit start when using addEventListener rather than onmessage. The missing-port-start mutant leaves a client connected but silent, which the lifecycle trace detects. Production code should also remove ports whose delivery fails and avoid treating the connection set as durable presence.

A coordinator simplifies ordering but adds an owner whose lifetime is not an application database. When the last eligible document disappears, the user agent may terminate the worker. Browser process pressure can also end it. A later tab can create a fresh worker with empty memory. Persist important state elsewhere and make initialization explicit.

This is why SharedWorker is not automatically “better synchronization.” It is useful when one volatile owner fits the product, such as multiplexing an expensive connection or serializing transient work. If state must survive restarts, the worker should coordinate a durable store rather than impersonate one.

Lifecycle cases expose the real trade-off

A matrix compares join, reload, last-client exit, worker restart, and unsupported capability behavior.

Lifecycle cases expose the real trade-off reading guideRead the evidence table from its row headers across the comparison columns; the text and verdict words carry every conclusion.
LifecycleBroadcastChannelSharedWorkerProduct requirement
Joinrequest peer snapshotworker sends canonical snapshotdeclare initial owner
Reloadnew generation reconcilesnew port reconnectsreject old generation
Last client exitsno retained bus stateworker may terminatepersist elsewhere if needed
Restartfind peer or durable sourcereinitialize workernever imply durability
Unavailablestorage/server fallbackpeer/server fallbackkeep semantics complete
Reload and restart behavior decide more than a synthetic message-rate benchmark.
  • Reload creates a new lifecycle generation.
  • Delayed messages from an old generation are ignored.
  • Closing all clients removes every volatile ownership path.
  • A durable source is a separate layer, not a hidden property of either API.

Reading rule: headers, values, verdict words, and sequence carry the conclusion; color is supplementary.

Handle joins, reloads, disconnects, and volatile lifetime

The shared trace begins with tab A, joins tab B, accepts one edit, delays a delivery, reloads B into a new generation, delivers the stale event, applies a new edit, closes A, exits the last client, and restarts. Every transition has an expected owner and recovery path. This lifecycle makes BroadcastChannel vs SharedWorker concrete without inventing a speed benchmark.

In the channel design, reload discards B’s local memory. The new document rejoins and requests a snapshot. A delayed event addressed to the old generation is ignored. If no peers remain, there may be no snapshot source; a durable database or server becomes necessary. The bus itself retains nothing.

In the worker design, reload reconnects to the same coordinator if it survived. After last-client exit and worker termination, the next worker starts from its declared initialization source. The lab deliberately initializes empty and labels the lost volatile revision. A restart-state-loss mutant incorrectly preserves memory, and the oracle rejects the fictional durability.

Product copy should describe these cases in user language: “changes sync while this browser session is open,” “saved changes survive closing all tabs,” or “another tab may have a newer draft.” Lifecycle promises are easier to test than vague real-time claims.

Reject stale, duplicate, and unauthorized messages

Every event crosses an untrusted asynchronous boundary, even inside one origin. Parse exact fields, bound string sizes, require finite integer revisions, reject unknown message types, and cap remembered IDs. A protocol that accepts extra fields can accidentally change meaning after a deployment when old and new tabs overlap. Neither side of BroadcastChannel vs SharedWorker removes this validation duty.

Stale base revisions should not become last-write-wins by accident. A production adapter should return a conflict outcome containing current revision and a reconciliation request. Duplicate command IDs should return the prior outcome without committing again. Unauthorized client roles should receive a policy rejection that does not disclose more state than needed. The lab measures stale and duplicate aggregate outcomes; authorization remains an explicit production requirement outside its trace.

This state ownership boundary relates to CSS :has() versus JavaScript UI state. CSS can derive present relationships, while cross-tab edits are durable or asynchronous facts that need an explicit reducer and often server policy. Moving messages between tabs does not change that ownership classification.

The protocol also needs version negotiation. A new tab should not send commands an old worker or peer cannot interpret. The bounded lab pins protocol version one and kills a missing-lifecycle-identity mutant. A production rollout may need dual readers, migration, or a forced refresh, but that is beyond this comparison.

One stale delivery takes two different repair pathsA revision timeline shows join, commit, delayed stale message, reload generation, reconciliation, duplicate rejection, and worker restart.TRACE 01 · REVISION OWNERSHIPPEERSWORKERjoinr0editr1delayr0reloadg2repairr1dupignoreconnectr0commitr1ackr1new portg2exitemptyrestartr0peer reconciles stale delivery · worker serializes until volatile owner ends
One stale delivery takes two different repair paths. The independent reducer compares domain outcomes even though the adapters emit different internal logs.
  1. Both designs begin at revision zero and commit the same first edit.
  2. The peer lane rejects a delayed revision-zero event after reload and requests repair.
  3. The worker lane serializes commands while alive and starts empty after declared termination.
  4. Duplicate IDs never create a second commit in either projected outcome.

Reading rule: labels, values, shapes, order, and line styles carry the conclusion; color is supplementary.

Replay one trace through both designs

A fair BroadcastChannel vs SharedWorker comparison gives both adapters the same sanitized event trace and evaluates domain outcomes, not internal event counts. The independent reducer consumes abstract actions—join, edit, deliver, reload, close, terminate—and produces expected canonical revisions, visible values, conflicts, ignored duplicates, and lost volatile state.

The bounded BroadcastChannel and SharedWorker adapters each produce one action-outcome row per frozen trace step plus one aggregate projection. They do not export peer-local views, reconciliation events, or client acknowledgements. The independent reducer therefore compares only the aggregate revision, visible value, commit, duplicate, stale, partition-rejection, restart, and live-client results represented in that receipt.

This avoids a misleading benchmark. A worker may send more or fewer messages depending on protocol shape; that alone says nothing about correctness or user experience. Real selection also depends on browser support, resource sharing, startup, memory, and background behavior, none of which a virtual trace measures.

The artifact labels actual API constructor availability separately from simulation. An available constructor does not prove lifetime or fitness, and an unavailable constructor does not invalidate the protocol model. Capability detection informs fallback; it does not change the recorded scenario.

Receipt validation is part of the replay rather than a ceremonial export step. The canonical JSON contains contexts, origin and partition identities, protocol version, full action trace, projected outcomes, both adapter logs, capability labels, named mutation results, and a hash. Validation reparses an exact key set, rebuilds the independent expected outcome, reruns both adapters from the inputs, and deep-compares every field.

CSV is an RFC-style envelope carrying the same canonical JSON, so it cannot omit a losing adapter’s conflict or a cleanup count. Hash checks are secondary. If an attacker changes a result and recomputes the stored hash, reconstruction still rejects the forged state.

Executable mutants cover sender self-delivery, stale acceptance, missing port.start, preserved worker memory after restart, duplicate application, partition confusion, and missing lifecycle identity. Each mutant changes code or policy and must produce a concrete aggregate oracle difference. Production validation should additionally reject unknown actions, oversized IDs, negative revisions, duplicate context identities, unsupported protocol versions, and unauthorized roles; those are requirements outside this seven-mutant trace, not extra artifact fixtures.

The external contracts come from the WHATWG BroadcastChannel definition, the SharedWorker model, and the Web Platform Tests worker suite. The lab tests its bounded protocol, not conformance of the browser running it.

Choose the smallest ownership model that stays correct

Choose BroadcastChannel when peers already own recoverable local state, notifications are enough, and explicit reconciliation is acceptable. Choose SharedWorker when one volatile browser coordinator materially simplifies ordering or resource ownership and a complete fallback exists. Choose neither as the sole durable database. BroadcastChannel vs SharedWorker remains a tab coordination choice, not a persistence strategy.

Write down the failure promise before the API choice. What happens after reload, after the last tab closes, during an old/new deployment overlap, when a stale edit arrives, and when the preferred primitive is unavailable? If the answer requires persistence or cross-device sync, place that concern in IndexedDB or a server and keep the tab primitive as coordination.

The broader Cache API versus IndexedDB comparison helps separate response caching from structured durable state. The WebSocket backpressure comparison similarly shows why transport mechanics do not decide application ownership. Architecture becomes clearer when each layer has one honest job.

The final BroadcastChannel vs SharedWorker decision is provisional and evidence-bound. This trace shows both bounded adapters reproduce the independent reducer’s final aggregate projection and that seven aggregate mutants diverge. It does not prove every per-client lifecycle, performance, availability, durability, cross-device behavior, or communication across different storage partitions. That is enough to choose responsibly without overselling either API. Keeping that boundary explicit makes a BroadcastChannel vs SharedWorker review useful after the prototype becomes a product.

Runnable local artifact — The trace proves one bounded protocol model, not browser availability, worker lifetime, performance, durability, cross-device synchronization, authorization, or behavior across different storage partitions.

Plain text1 line
Replay the fixed join/edit/delay/reload/duplicate/close/restart trace through peer and coordinator adapters; inspect lifecycle outcomes, mutants, and strict JSON/CSV receipts.