Editor Embed
/
With the SDK
Editor Embed with the SDK
Plan access
Editor Embeds are available on Free, Pro, and Enterprise, within each plan's usage limits.
Embed the full Moshpit editor inside your own UI. Users edit, save, and publish without leaving your app — and you stay in control of the surrounding chrome.
Minimal example
HTML
<div id="moshpit-editor" style="height: 720px"></div>
<script src="https://moshpit.studio/moshpit-embed.js"></script>
<script>
const editor = Moshpit.editor('#moshpit-editor', {
publicKey: 'mpk_PUBLIC_KEY',
sessionEndpoint: '/api/moshpit/editor-session',
splatId: 'OPTIONAL_SPLAT_ID',
});
editor.on('ready', () => console.log('editor ready'));
editor.on('saved', ({ splatId }) => console.log('saved', splatId));
editor.on('authRequired', () => showSignInPrompt());
editor.on('dirtyChanged', ({ dirty }) => {
saveBtn.disabled = !dirty;
});
</script>The matching backend endpoint mints an editor session — type 'editor' instead of 'viewer':
TypeScript
// POST /api/moshpit/editor-session
export async function POST(request: Request) {
const body = await request.json().catch(() => ({}));
const upstream = await fetch(
'https://moshpit.studio/api/editor/embed-sessions',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.MOSHPIT_SECRET_KEY}`,
},
body: JSON.stringify({
publicKey: process.env.MOSHPIT_PUBLIC_KEY,
type: 'editor',
splatId: body.splatId ?? null,
}),
},
);
return new Response(await upstream.text(), {
status: upstream.status,
headers: { 'Content-Type': 'application/json' },
});
}If you mint a blank guest editor session without an external user, listen for
authRequired. When it fires, sign the user into your app, mint a scoped editor
session, and send it back with updateSession before retrying save.
Open an existing splat or create a new one
Pass splatId to open an existing scene. Omit it (or pass null) to start in "create new" mode:
JavaScript
// Edit an existing splat
Moshpit.editor('#moshpit-editor', {
publicKey: 'mpk_...',
sessionEndpoint: '/api/moshpit/editor-session',
splatId: '65f1a2b3c4d5e6f7a8b9c0d1',
});
// Start with a blank canvas
Moshpit.editor('#moshpit-editor', {
publicKey: 'mpk_...',
sessionEndpoint: '/api/moshpit/editor-session',
});When the user saves a brand-new splat, the saved event delivers the new splatId:
JavaScript
editor.on('saved', ({ splatId }) => {
// First save → splatId is now defined
// Persist this on your side and update the host URL if you want reloads to reopen it
if (splatId) {
const url = new URL(window.location.href);
url.searchParams.set('splatId', splatId);
history.replaceState(null, '', `${url.pathname}${url.search}${url.hash}`);
}
});Drive the editor from your UI
Wire your own buttons up to the editor's commands:
JavaScript
saveBtn.addEventListener('click', () => editor.save());
publishBtn.addEventListener('click', () => editor.publish());
newBtn.addEventListener('click', () => editor.createProject());
openBtn.addEventListener('click', () => editor.openProject(splatId));The full list lives at Editor Commands.
Track unsaved changes
dirtyChanged lets you reflect unsaved state in your UI — disable navigation, show a dot in the title bar, prompt before unmount:
JavaScript
let isDirty = false;
editor.on('dirtyChanged', ({ dirty }) => {
isDirty = dirty;
saveBtn.disabled = !dirty;
});
window.addEventListener('beforeunload', (e) => {
if (isDirty) {
e.preventDefault();
e.returnValue = '';
}
});Tear down
Single-page-app users may navigate away. Call editor.destroy() to remove the iframe and clean up listeners and refresh timers.
JavaScript
editor.destroy();What's next
- Editor → Plain Iframe — when you don't want the SDK.
- Editor → Next.js Integration — App Router and Pages Router.
- Editor Events and Editor Commands — full surface.