Skip to content

Channel

channel.ts wraps the browser’s BroadcastChannel API. All cross-role messaging goes through it.

export type ChannelMessage =
| { type: 'show'; target: ScreenId; itemId: ItemId }
| { type: 'clear'; target: ScreenId }

Every message names a single target screen. Multiple screens can be addressed only by sending multiple messages (e.g. the simulator’s Reset iterates over Object.keys(settings.screens)).

import { createChannel } from '../channel'
const channel = createChannel()
// Send (controller)
channel.send({ type: 'show', target: 'screen-1', itemId: 'a' })
// Receive (screen)
const off = channel.onMessage((msg) => {
if (msg.target !== myId) return
if (msg.type === 'show') setItem(getItem(msg.itemId))
if (msg.type === 'clear') setItem(null)
})
// Clean up
off()
channel.close()
  • Same origin, same machine. All open browser contexts (tabs, windows, iframes) on the same origin receive each other’s messages.
  • The sender does not receive its own message. Senders update their local state directly.

For walls spread across machines, the same API can be backed by a tiny WebSocket relay (Node + ws, ~50 lines). The shape of createChannel() stays the same; only the implementation changes. Call sites don’t move.