Appearance
Slots Communication
Each slot of your extension runs in its own iframe. To send data between slots, the platform provides a whisper mechanism — a messaging system that lets your extension frames communicate with each other.
There are two whisper scopes:
| Request | Event | Scope |
|---|---|---|
v1.ext.whisper | v1.ext.whispered | All clients — broadcasts to every user (viewers + model) who has this extension loaded in the stream. Goes through the server. |
v1.ext.whisper.local | v1.ext.whispered | Current browser tab only — reaches other iframes of the same extension within the same tab. Stays client-side. |
v1.ext.whisper (Broadcast)
Sends a message through the server to all clients in the stream that have this extension loaded. Every slot iframe of every user receives the v1.ext.whispered event.
When a user sends a broadcast whisper during a public show, the paymentData field is required. Obtain it from the v1.payment.tokens.spend.succeeded event. The model can whisper without paymentData. See Whisper with Payment Data for the full flow.
Use this when you need to synchronize state across all users — for example, broadcasting a game event, updating a shared scoreboard, or notifying everyone about a state change.
Sending
js
await ext.makeRequest('v1.ext.whisper', {
data: {
type: 'GAME_STARTED',
round: 1,
},
});Receiving
js
ext.subscribe('v1.ext.whispered', (data) => {
if (data.type === 'GAME_STARTED') {
foo(data.round);
}
});v1.ext.whisper.local (Local)
Sends a message to other iframes of the same extension within the current browser tab. The message does not leave the client — it is not sent to the server and other users will not receive it.
Use this for coordination between your own slots — for example, the EXTENSION_SLOT_BACKGROUND slot telling EXTENSION_SLOT_RIGHT_OVERLAY to update its UI, or EXTENSION_SLOT_MAIN_GAME_FUN notifying EXTENSION_SLOT_BACKGROUND that the user performed an action.
Sending
js
await ext.makeRequest('v1.ext.whisper.local', {
data: {
type: 'UPDATE_UI',
payload: { score: 42 },
},
});Receiving
js
ext.subscribe('v1.ext.whispered', (data) => {
if (data.type === 'UPDATE_UI') {
bar(data.payload.score);
}
});When to Use Which
| Scenario | Request |
|---|---|
| Notify all viewers about a game event | v1.ext.whisper |
| Sync shared state (scoreboard, timer) across all users | v1.ext.whisper |
| Coordinate between your own slots in the same tab | v1.ext.whisper.local |
EXTENSION_SLOT_BACKGROUND slot instructing EXTENSION_SLOT_RIGHT_OVERLAY to re-render | v1.ext.whisper.local |
| Model's action should be visible to all viewers | v1.ext.whisper |
Data Format
The data field accepts any JSON-serializable object:
js
await ext.makeRequest('v1.ext.whisper.local', {
data: {
type: 'MY_EVENT',
payload: { /* any JSON-serializable data */ },
},
});TIP
Use a type field in your whisper data to distinguish between different message types. This makes it easy to handle multiple message types in a single subscriber.
Example: Background Slot as Coordinator
A common pattern is to use the EXTENSION_SLOT_BACKGROUND slot as a central coordinator. Since it is never unmounted, it can maintain state and relay messages between visual slots.
js
// background slot
const ext = createExtHelper();
let gameState = { round: 0, scores: {} };
ext.subscribe('v1.ext.whispered', (data) => {
if (data.type === 'PLAYER_ACTION') {
gameState = bar(gameState, data);
// relay updated state to local UI slots
ext.makeRequest('v1.ext.whisper.local', {
data: { type: 'STATE_UPDATE', state: gameState },
});
}
});js
// EXTENSION_SLOT_RIGHT_OVERLAY slot
const ext = createExtHelper();
ext.subscribe('v1.ext.whispered', (data) => {
if (data.type === 'STATE_UPDATE') {
foo(data.state);
}
});Whisper with Payment Data
When a user sends a broadcast whisper in a public show, the platform requires paymentData to validate the request. This means the user must first spend tokens via v1.payment.tokens.spend, then pass the resulting paymentData into the whisper call.
Example
js
const ext = createExtHelper();
// 1. Request a token spend
await ext.makeRequest('v1.payment.tokens.spend', {
tokensAmount: 10,
tokensSpendData: { action: 'spin_wheel' },
});
// 2. On payment confirmation, send the whisper with paymentData
ext.subscribe('v1.payment.tokens.spend.succeeded', async (data) => {
await ext.makeRequest('v1.ext.whisper', {
data: { type: 'SPIN_RESULT', value: 7 },
paymentData: data.paymentData,
});
});
// 3. All clients receive the whispered event
ext.subscribe('v1.ext.whispered', (data) => {
if (data.type === 'SPIN_RESULT') {
showResult(data.value);
}
});