Embed API (Viewer Control)
Control the embedded Splat Labs viewer from your own website β fly visitors to named locations, list a project's stops programmatically, and react when the camera arrives
Embed API
Build deeper interactivity between your website and an embedded Splat Labs viewer. With the Embed API, your page can fly the visitor's camera to named locations in the scene, enumerate a project's locations programmatically, and react when navigation completes β all from your own UI.
Typical uses: real-estate tour pages with room-by-room navigation buttons, product showrooms that sync scene position with page content, and facility documentation that deep-links into specific areas.
Enterprise feature
The Embed API is available on Enterprise plans and is enabled per account. Contact us or email support@splatlabs.ai with the exact origins of your site (for example https://example.com and https://www.example.com) and we'll allowlist them for all your projects β current and future.
How it works
The API is a small postMessage surface between your page and the viewer iframe β no SDK, no tokens, no extra HTTP calls. Without an allowlist entry the embed still displays and all built-in viewer controls work, but messages from your page are ignored.
Requirements:
- Link sharing enabled on the project (this is what makes the embed publicly viewable).
- Your site's origins allowlisted for your account (see above).
Name your locations
Locations are stops β named camera viewpoints authored in the viewer's Collections editor (for example Kitchen, Master Bedroom, Loading Dock). Add as many per project as you like. Your page addresses them by name or by id.
Embed the viewer
All messaging is with this iframe. Always pass the explicit target origin https://cloud.splatlabs.ai when sending, and verify event.origin === 'https://cloud.splatlabs.ai' when receiving.
Commands (your page β viewer)
Send with iframe.contentWindow.postMessage(message, 'https://cloud.splatlabs.ai').
Fly to a stop
Name matching is exact after trimming, case-insensitive, first match in collection order. No match β a navigationEnded event with status: 'not_found' and no camera movement.
Every gotoStop produces exactly one navigationEnded event. Send a second one while a flight is still in progress and the first is closed out with status: 'superseded' β the newer command wins. You do not need to serialise commands yourself, though addressing one stop at a time still gives the smoothest result for a visitor.
List a project's stops
Replies with a stopsAvailable event (below).
Walk to an annotation (advanced)
Unlike gotoStop (a direct camera flight), this plans a walking route along the floor to an annotation marker, and returns status: 'error' on scenes without navigation data. Prefer gotoStop unless you specifically want the walking behavior.
Events (viewer β your page)
Delivered to your window via postMessage from origin https://cloud.splatlabs.ai.
stopsAvailable β the stop list and your ready signal
Sent once automatically as soon as the viewer can serve navigation commands (with stops: [] if the project has no collections), and again as the reply to every getStops. Commands sent before this event arrives may be dropped β build your navigation UI in response to it.
getStops is answered as soon as the viewer's message channel is live, which can be earlier than the automatic push. Polling it until it answers is a valid alternative readiness check if you prefer not to depend on the unsolicited event.
navigationEnded β navigation outcome
Emitted only for navigations your page initiated β visitor-driven navigation inside the viewer does not produce this event.
status | Meaning |
|---|---|
arrived | The camera reached the destination. |
error | The stop was found but the flight failed. |
not_found | No stop matched the id or name you sent. The camera did not move. |
superseded | A newer gotoStop arrived while this one was still in flight. Expect a separate event for that newer command. |
Putting it together
Good practice
- Address by
id(fromstopsAvailable) when you want wiring that survives stop renames. - Keep stop names unique per project β name lookup is first-match-wins.
- No delivery receipts. A command the viewer never receives (origin not allowlisted, viewer not ready, unknown type) produces no event at all. Once a
gotoStopis received it always resolves to exactly onenavigationEnded, so a missing event after a few seconds means the send itself failed β not that the navigation is still running. - Multiple viewers on one page: track which iframe each event came from via
event.source.
Stability
The commands and events on this page are a supported contract: we won't rename fields or change semantics without advance notice and a deprecation window communicated to enabled accounts. Additions (new optional fields, new commands or events) may happen without notice β write your handlers to ignore unknown fields and types. Anything you may observe on the message channel that is not documented here is internal and unsupported.