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
| Attribute | Value | Why |
|---|---|---|
allow | fullscreen; clipboard-read; clipboard-write; file-system | Editor uses clipboard for paste/copy ops and local folder import |
allowfullscreen | (boolean) | Older browser fullscreen flag |
referrerpolicy | strict-origin-when-cross-origin | Don'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:
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
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':
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
- Editor Commands — full command catalog.
- Editor Events — full event catalog.
- PostMessage Protocol — the message envelope reference.