Editor Embed

/

Plain Iframe

Editor Embed with a Plain Iframe

Plan access

Editor Embeds are available on Free, Pro, and Enterprise, within each plan's usage limits.

The editor works as a regular iframe when you can't use the SDK. The mechanics mirror the viewer iframe flow, with one extra responsibility: you have to refresh the session yourself for sessions that outlive 15 minutes.

URL pattern

–

Plain

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

splatId is optional. Omit it to start a new project.

Hard-coded src will fail

An iframe pointed at the editor URL without a session token shows "Editor session is required" and refuses to load. Always fetch the session before setting src.

Minimal example

<>

HTML

<iframe
  id="moshpit-editor"
  width="100%"
  height="720"
  style="border: 0"
  allow="fullscreen; clipboard-read; clipboard-write; file-system"
  allowfullscreen
  referrerpolicy="strict-origin-when-cross-origin"
></iframe>
 
<script>
  let currentSession = null;
 
  async function loadMoshpitEditor() {
    const response = await fetch('/api/moshpit/editor-session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ splatId: 'OPTIONAL_SPLAT_ID' }),
    });
 
    if (!response.ok) throw new Error('Unable to create editor session');
 
    currentSession = await response.json();
    const url = new URL('https://moshpit.studio/editor/embed/mpk_PUBLIC_KEY');
    url.searchParams.set('session', currentSession.sessionToken);
    url.searchParams.set('splatId', 'OPTIONAL_SPLAT_ID');
 
    document.getElementById('moshpit-editor').src = url.toString();
    scheduleRefresh();
  }
 
  loadMoshpitEditor();
</script>

Backend session route is identical to the viewer's, except type: 'editor' instead of type: 'viewer'. See Editor → SDK for the snippet.

Required iframe attributes

AttributeValueWhy
allowfullscreen; clipboard-read; clipboard-write; file-systemEditor uses clipboard for paste/copy ops and local folder import
allowfullscreen(boolean)Older browser fullscreen flag
referrerpolicystrict-origin-when-cross-originDon't leak the host page's full URL

Refresh the session before it expires

Editor sessions expire after 15 minutes. Without the SDK you have to refresh manually. The pattern: schedule a refresh roughly 60 seconds before the current expiresAt, fetch a new token, and push it into the iframe with the updateSession command:

JS

JavaScript

function scheduleRefresh() {
  if (!currentSession?.expiresAt) return;
  const ms = new Date(currentSession.expiresAt).getTime() - Date.now() - 60_000;
  setTimeout(refreshSession, Math.max(ms, 5_000));
}
 
async function refreshSession() {
  const res = await fetch('/api/moshpit/editor-session', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ splatId: 'OPTIONAL_SPLAT_ID' }),
  });
  if (!res.ok) {
    console.error('Session refresh failed');
    return;
  }
 
  currentSession = await res.json();
  const iframe = document.getElementById('moshpit-editor');
 
  iframe.contentWindow.postMessage(
    {
      source: 'moshpit-sdk',
      type: 'command',
      target: 'editor',
      command: 'updateSession',
      value: {
        sessionToken: currentSession.sessionToken,
        expiresAt: currentSession.expiresAt,
      },
    },
    'https://moshpit.studio',
  );
 
  scheduleRefresh();
}

You can also listen for the sessionExpiring event broadcast by the iframe and trigger refresh there. See Session Tokens for both patterns.

Listen for events without the SDK

JS

JavaScript

const iframe = document.getElementById('moshpit-editor');
 
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-editor') return;
 
  switch (data.type) {
    case 'ready':
      // Editor finished loading
      break;
    case 'saved':
      if (data.payload?.splatId) {
        const url = new URL(window.location.href);
        url.searchParams.set('splatId', data.payload.splatId);
        history.replaceState(
          null,
          '',
          `${url.pathname}${url.search}${url.hash}`,
        );
      }
      break;
    case 'published':
      console.log('published', data.payload);
      break;
    case 'dirtyChanged':
      saveBtn.disabled = !data.payload.dirty;
      break;
    case 'authRequired':
      showSignInPrompt(data.payload?.message);
      break;
    case 'sessionExpiring':
      refreshSession();
      break;
    case 'error':
      console.error(data.payload.message);
      break;
  }
});

The full list of events is at Editor Events.

Send commands without the SDK

Same envelope as the viewer, with target: 'editor':

JS

JavaScript

const iframe = document.getElementById('moshpit-editor');
 
iframe.contentWindow.postMessage(
  { source: 'moshpit-sdk', type: 'command', target: 'editor', command: 'save' },
  'https://moshpit.studio',
);
 
iframe.contentWindow.postMessage(
  {
    source: 'moshpit-sdk',
    type: 'command',
    target: 'editor',
    command: 'openProject',
    value: { splatId: '65f1a2b3c4d5e6f7a8b9c0d1' },
  },
  'https://moshpit.studio',
);

What's next