Viewer Embed
/
Events
Viewer Events
The viewer reports interactions back to the host page. Subscribe with viewer.on(...) when using the SDK or with a window.addEventListener('message', ...) listener when not.
With the SDK
JavaScript
const viewer = Moshpit.viewer('#moshpit-viewer', {
publicKey: 'mpk_...',
sessionEndpoint: '/api/moshpit/viewer-session',
splatId: 'YOUR_SPLAT_ID',
});
const off = viewer.on('viewTracked', ({ viewCount, isNewView }) => {
console.log('view tracked', { viewCount, isNewView });
});
// Unsubscribe later
off();.on(...) returns an unsubscribe function. Calling viewer.destroy() removes all listeners automatically.
Event reference
| Event | Payload | Fires |
|---|---|---|
ready | undefined | When the first frame has rendered. Anything you call before this fires is queued. |
viewTracked | { viewCount?: number; isNewView?: boolean } | When Moshpit registers a view for analytics. `isNewView` is true the first time a unique visitor opens this splat. |
fullscreenChanged | { fullscreen: boolean } | When the user enters or exits fullscreen. |
sessionExpiring | { expiresAt: string } | About 60 seconds before the session JWT expires. The SDK uses this internally to refresh; intercept it only if you also need to. |
qualityChanged | { quality: "high" | "medium" | "low" } | When the active Viewer quality preset changes. setQuality("auto") reports the resolved default preset, currently "medium". |
loaded | { splatId: string; source: "gallery" | "embed"; hasLods: boolean } | When the Scene finishes loading. |
loadError | { message: string } | When the Scene fails to load. |
camera-change | { position: { x: number; y: number; z: number }; rotation: { yaw: number; pitch: number; roll: number } } | When the embedded Viewer camera pose changes. |
scene-change | { sourceSplatId: string; destinationSplatId: string } | After portal travel loads a different Scene. |
cloned | { splatId: string; sourceSplatId: string } | After the viewer's Remix button successfully copies the scene. `splatId` is the new copy; `sourceSplatId` is the scene that was remixed. The viewer does not navigate — decide what to do with the new scene yourself (open it, refresh a gallery, etc.). |
hostFullscreenRequested | { lockLandscape?: boolean; reason?: "mobile-landscape-interaction" } | When the host page should fullscreen its Viewer container for the embedded mobile experience. |
hostFullscreenExitRequested | undefined | When the host page should exit its Viewer-container fullscreen. |
error | { message: string } | Compatibility event emitted alongside `loadError` when the Scene fails to load. |
Without the SDK
Listen on window, validate the origin, and check the source discriminator:
JavaScript
const iframe = document.getElementById('moshpit-viewer');
window.addEventListener('message', (event) => {
if (event.origin !== 'https://moshpit.studio') return;
if (event.source !== iframe.contentWindow) return;
const data = event.data;
if (data?.source !== 'moshpit-viewer') return;
switch (data.type) {
case 'ready':
// First frame rendered
break;
case 'viewTracked':
console.log(data.payload.viewCount, data.payload.isNewView);
break;
case 'fullscreenChanged':
console.log(data.payload.fullscreen);
break;
case 'sessionExpiring':
// Refresh the session — see Session Tokens
break;
case 'cloned':
console.log('remixed', data.payload.splatId, data.payload.sourceSplatId);
break;
case 'loadError':
case 'error':
console.error(data.payload.message);
break;
}
});The full message envelope is documented at PostMessage Protocol.
Always validate the sender
Check both the Moshpit event.origin and the iframe event.source. The SDK
does this for you; raw addEventListener listeners must do it themselves.
What's next
- Viewer Commands — what you can send back.
- Editor Events — the editor's much larger event surface.