Viewer Embed

/

Plain Iframe

Viewer Embed with a Plain Iframe

You don't have to ship the SDK. The viewer works as a regular iframe — useful when you can't add a third-party script, when you want full control over the DOM, or when CSP rules block external scripts.

URL pattern

–

Plain

https://moshpit.studio/viewer/embed/{publicKey}?session={sessionToken}&splatId={splatId}

The iframe needs both the session query param and a valid splatId. Without a session it shows a "Viewer session is required" page.

Minimal example

You can't hard-code a session token in HTML — they expire in 15 minutes. Always fetch one from your backend first, then set the src:

<>

HTML

<iframe
  id="moshpit-viewer"
  width="100%"
  height="600"
  style="border: 0"
  allow="fullscreen; microphone"
  allowfullscreen
  loading="lazy"
  referrerpolicy="strict-origin-when-cross-origin"
></iframe>
 
<script>
  async function loadMoshpitViewer() {
    const response = await fetch('/api/moshpit/viewer-session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ splatId: 'YOUR_SPLAT_ID' }),
    });
 
    if (!response.ok) throw new Error('Unable to create viewer session');
 
    const { sessionToken } = await response.json();
 
    const url = new URL('https://moshpit.studio/viewer/embed/mpk_PUBLIC_KEY');
    url.searchParams.set('session', sessionToken);
    url.searchParams.set('splatId', 'YOUR_SPLAT_ID');
 
    document.getElementById('moshpit-viewer').src = url.toString();
  }
 
  loadMoshpitViewer();
</script>

The matching backend route lives in Quickstart.

AttributeValueWhy
allowfullscreenAllow the user to enter fullscreen mode
allowfullscreen(boolean)Older browsers' fullscreen flag
loadinglazyLet the browser defer loading until the iframe is near the viewport
referrerpolicystrict-origin-when-cross-originDon't leak the host page's full URL

width="100%" and a fixed height on the iframe is the simplest layout. For responsive embeds, wrap the iframe in a 16:9 aspect-ratio container.

This iframe attribute controls the request from your page to the Viewer Embed. The hosted viewer separately uses Referrer-Policy: no-referrer for its own Scene-file and API requests.

Listening for events without the SDK

The iframe sends events to its immediate parent via postMessage. Listen for them on window:

JS

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':
      console.log('viewer ready');
      break;
    case 'viewTracked':
      console.log('view tracked', data.payload);
      break;
    case 'sessionExpiring':
      // Refresh the session — see Session Tokens
      break;
  }
});

The full event catalog is at Viewer Events.

Sending commands without the SDK

Send commands the same way the SDK does — a postMessage with the moshpit-sdk source:

JS

JavaScript

const iframe = document.getElementById('moshpit-viewer');
 
iframe.contentWindow.postMessage(
  {
    source: 'moshpit-sdk',
    type: 'command',
    target: 'viewer',
    command: 'setQuality',
    value: 'high',
  },
  'https://moshpit.studio',
);

Always pass the explicit target origin ('https://moshpit.studio') — never '*' — so messages can't be intercepted by other frames.

The full command catalog is at Viewer Commands.

Manual session refresh

Plain iframes don't auto-refresh sessions. Either:

  • Listen for the sessionExpiring event and call your session endpoint, then send an updateSession command back into the iframe.
  • Set a timer slightly before the token's expiresAt and refresh proactively.

Both patterns are spelled out in Session Tokens → Refresh before expiry.

What's next