# How to Use the SuperSplat Iframe API for External Integration

> Integrate SuperSplat into your projects with the iframe API. Communicate with the editor via postMessage to query scene state and receive typed responses.

- Repository: [PlayCanvas/supersplat](https://github.com/playcanvas/supersplat)
- Tags: how-to-guide
- Published: 2026-05-10

---

**The SuperSplat iframe API enables external web applications to communicate with the embedded editor via `window.postMessage`, allowing parent windows to query internal scene state such as unsaved changes and receive typed responses.**

SuperSplat, the open-source Gaussian Splat editor maintained by PlayCanvas, exposes a lightweight message-based API for secure cross-origin integration. Located in the `playcanvas/supersplat` repository, this API—implemented primarily in [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts)—allows developers to embed the 3D editor within host applications and programmatically inspect editor state without direct DOM manipulation.

## Understanding the SuperSplat Iframe API Architecture

The API implementation attaches a `message` event listener to the iframe's `window` object inside [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts). When a parent window posts a message matching a supported query shape, SuperSplat validates the payload using TypeScript type guards and invokes internal functions through the shared `Events` object defined in [`src/events.ts`](https://github.com/playcanvas/supersplat/blob/main/src/events.ts).

The communication follows a strict request-response pattern:

1. The **parent page** sends a JSON message via `postMessage` containing `{ type: "supersplat:is-scene-dirty" }`
2. **SuperSplat** receives the message, validates it using the `isSceneDirtyQuery` guard, and calls `events.invoke('scene.dirty')` to check the internal state
3. SuperSplat replies with `{ type: "supersplat:is-scene-dirty", result: <boolean> }` posted back to the originating window's origin

Because the system relies on standard `window.postMessage`, it works securely across different origins while keeping the editor's internal implementation encapsulated.

## Querying Scene State from the Parent Window

The current API implementation supports checking whether the active scene contains unsaved modifications. This is essential for host applications that need to display save prompts or prevent accidental data loss before closing the editor.

### Checking for Unsaved Changes

The `isSceneDirtyQuery` function in [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts) validates incoming messages before processing. When invoked, it returns `true` if pending changes exist in the scene, or `false` if the state is saved.

```html
<!-- parent.html -->
<iframe id="splatIframe" src="https://your.domain/supersplat.html"></iframe>

<script>
const iframe = document.getElementById('splatIframe');

// Ask SuperSplat whether the scene is dirty
function askIfSceneDirty() {
    const msg = { type: 'supersplat:is-scene-dirty' };
    iframe.contentWindow.postMessage(msg, '*');
}

// Listen for SuperSplat's response
window.addEventListener('message', (e) => {
    const data = e.data;
    if (data && data.type === 'supersplat:is-scene-dirty') {
        console.log('Scene dirty?', data.result); // true | false
    }
});

// Example usage
askIfSceneDirty();
</script>

```

The handler specifically checks `event.data.type` to filter SuperSplat messages from other `postMessage` traffic, ensuring your application only processes relevant responses.

## Integrating SuperSplat into React Applications

For React-based host applications, you can wrap the iframe communication in a functional component using `useRef` and `useEffect` hooks. This pattern maintains type safety and ensures proper cleanup of global event listeners when the component unmounts.

```tsx
import React, { useEffect, useRef } from 'react';

const SuperSplatHost = () => {
  const iframeRef = useRef<HTMLIFrameElement>(null);

  // Ask the iframe if the scene is dirty
  const checkDirty = () => {
    iframeRef.current?.contentWindow?.postMessage(
      { type: 'supersplat:is-scene-dirty' },
      '*'
    );
  };

  // Handle the reply
  useEffect(() => {
    const handler = (e: MessageEvent) => {
      const d = e.data;
      if (d?.type === 'supersplat:is-scene-dirty') {
        alert(`Scene dirty: ${d.result}`);
      }
    };
    window.addEventListener('message', handler);
    return () => window.removeEventListener('message', handler);
  }, []);

  return (
    <>
      <button onClick={checkDirty}>Is Scene Dirty?</button>
      <iframe
        ref={iframeRef}
        src="https://your.domain/supersplat.html"
        style={{ width: '100%', height: '80vh', border: 'none' }}
      />
    </>
  );
};

export default SuperSplatHost;

```

## Extending the Iframe API with Custom Queries

While the current implementation only exposes the scene dirty check, the modular architecture in [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts) supports straightforward extension. Developers can add new query types by defining TypeScript interfaces, adding validation guards, and wiring handlers to invoke methods on the `Events` object.

To add a camera state query, modify [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts):

```ts
// In src/iframe-api.ts – add a new query type
const GET_CAMERA_STATE = 'supersplat:get-camera-state';

interface GetCameraStateQuery {
  type: typeof GET_CAMERA_STATE;
}

interface GetCameraStateResponse {
  type: typeof GET_CAMERA_STATE;
  result: { position: number[]; rotation: number[] };
}

const getCameraStateQuery = (data: any): data is GetCameraStateQuery =>
  data && typeof data === 'object' && data.type === GET_CAMERA_STATE;

// Inside the message listener …
if (getCameraStateQuery(event.data)) {
  const cam = events.invoke('camera.getState') as any;
  const response: GetCameraStateResponse = {
    type: GET_CAMERA_STATE,
    result: cam,
  };
  source.postMessage(response, event.origin);
}

```

After rebuilding the application, parent windows can request camera data by posting `{ type: 'supersplat:get-camera-state' }` and receiving the position and rotation arrays in the response. This pattern can be replicated for any internal state accessible through the `Events` system defined in [`src/events.ts`](https://github.com/playcanvas/supersplat/blob/main/src/events.ts).

## Summary

- The SuperSplat iframe API in [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts) provides secure, cross-origin communication via `postMessage`
- Query the scene dirty state by sending `{ type: 'supersplat:is-scene-dirty' }` to the iframe content window
- Responses return through the parent window's message event listener with matching type identifiers and boolean results
- The API leverages the `Events` object from [`src/events.ts`](https://github.com/playcanvas/supersplat/blob/main/src/events.ts) to safely invoke internal editor functions without exposing the full application state
- Developers can extend the system by adding new query validators and response handlers following the established pattern in [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts)

## Frequently Asked Questions

### Does the SuperSplat iframe API support cross-origin communication?

Yes, the API is intentionally built on `window.postMessage`, which inherently supports secure cross-origin communication. As implemented in [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts), SuperSplat validates incoming message shapes using TypeScript type guards like `isSceneDirtyQuery` before processing requests, ensuring that only properly formatted messages from any origin can query the editor's internal state.

### What message types does the SuperSplat iframe API currently support?

As of the latest source code in the `playcanvas/supersplat` repository, the only built-in query type is `supersplat:is-scene-dirty`. This specific message returns a boolean indicating whether the current scene has unsaved changes. However, the architecture explicitly supports extension, allowing developers to add new query types by defining additional interfaces and validation functions in [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts).

### How does SuperSplat validate incoming API messages?

SuperSplat validates messages using TypeScript type guard functions defined in [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts). For example, the `isSceneDirtyQuery` function strictly checks that the incoming data is an object containing `type: 'supersplat:is-scene-dirty'`. After validation succeeds, the handler calls `events.invoke('scene.dirty')` through the event system defined in [`src/events.ts`](https://github.com/playcanvas/supersplat/blob/main/src/events.ts) to retrieve the internal state before posting the result back to the parent window.

### Can I trigger actions in SuperSplat from the parent window, or only query state?

The current implementation in [`src/iframe-api.ts`](https://github.com/playcanvas/supersplat/blob/main/src/iframe-api.ts) focuses on querying read-only state rather than triggering mutations. The existing pattern uses `events.invoke()` to safely read values like the dirty status without modifying the scene. To trigger actions such as saving or transforming objects, you would need to extend the API by adding new message handlers that invoke appropriate mutating methods on the `Events` object, following the same validation and origin-checking patterns established for the scene dirty query.