Skip to content

Embeddable Session Replay for partners

Embeddable Session Replay puts a light version of Session Replay inside your own website or dashboard, so your customers can review participant sessions captured by Cognitive3D without a Cognitive3D account or login. Your backend mints a short-lived token scoped to a single participant, so each of your customers sees only the sessions you scope to them.

This is an early release. To request access, contact us with the Intercom button (purple circle) below.

Embeddable Session Replay proof-of-concept

The screenshot below shows Embeddable Session Replay running inside a partner's own analytics dashboard.

Embeddable Session Replay running in a partner dashboard, showing a warehouse training scene with a participant avatar, gaze cone, and controller overlay

The viewer renders in an iframe alongside the partner's own navigation and participant list. The controller overlay, play space boundary, and gaze cone come from the embed, not from the host page.

Authentication architecture

Embeddable Session Replay requires an Organization on the Cognitive3D platform with at least one Project. With the Project, Scene, and Session identifiers from the platform, you can embed the viewer on your own website using an iframe.

The diagram below shows how a credential travels from your backend to the embedded viewer.

Architecture diagram showing the partner backend requesting a user-level JWT from the Cognitive3D API server, passing it to the partner frontend, which posts it to the Embeddable Session Replay iframe

Your backend holds the organization API key and exchanges it for a short-lived token scoped to one participant. That token is the only credential that reaches the browser.

Token types

The platform provides two token types with different permission scopes.

Token Scope Where it lives
Organization API key Read and write across every project in the organization. Your backend only.
User-level JWT Read access to a single project, scoped to one participant. Your frontend and the embed.

Scoping tokens this way lets you manage and secure access for your own customers, and prevents one customer from viewing another customer's sessions. To integrate, you use the organization API key to generate a user-level JWT.

Warning

Never ship the organization API key to a browser. It grants read and write access to every project in your organization.

1. Request a user-level JWT with the organization API key

Your backend calls the Cognitive3D API to mint a scoped access token, authenticating with the organization API key. Scope the request to a participant ID and a project ID. The organization API key never leaves your backend and is never exposed to the browser.

2. Cognitive3D issues a user-level JWT

The Cognitive3D API signs and returns a short-lived JWT scoped to that single participant. The token authorizes exactly what the embedded viewer needs and nothing more: project and objective metadata such as names and labels, scene and object meshes, and session data for the scoped participant including objective results.

The token carries its own expiry, so both your app and the embed can tell when it lapses. There is no per-token revocation list. In an emergency, Cognitive3D rotates the signing key.

3. Pass the JWT to your frontend

Your backend hands the token to your own frontend as part of rendering the page for that customer, typically alongside the rest of that customer's page payload. Because the token is already scoped to one participant, the frontend cannot widen its reach: each customer reaches only the participant you scoped the token to, with no client-side permission logic to get wrong. Feature-level gating per customer is not part of this release, so all embeds unlock the same capability set.

4. Pass the JWT to the iframe with postMessage

The parent page delivers the token to the Session Replay iframe over the postMessage Web API rather than in the iframe src. This keeps the credential out of the URL, out of browser history, and out of referrer headers and server logs. The embed holds its requests until the token arrives, then boots and authenticates its API calls with it.

When the token nears or reaches expiry, the embed signals the parent frontend, which requests a fresh token through steps 1 to 3 and posts it back in. Replay continues without a reload.

Integration steps

Generate an organization API key

Create a read-write organization API key on the Cognitive3D Dashboard. Your backend uses this key to mint user-level JWTs.

  1. Open the account menu in the top right of the Dashboard and select Organization Settings.

    The Cognitive3D Dashboard account menu with Organization Settings highlighted

    The same menu holds Project Settings and Manage Developer Key. You need the organization scope, not the project scope.

  2. Select Manage API Keys in the left sidebar. Press Create API Key.

    The Manage API Keys page with the Manage API Keys sidebar entry and the Create API Key button highlighted

    The list shows each existing key by its last four characters, when it was last used, its status, and its permission level.

  3. Enter a description. Set Permissions to Read-Write. Press Generate API Key.

    The Create API Key dialog with the Permissions dropdown set to Read-Write and the Generate API Key button highlighted

    Read-Write is required. A read-only key cannot mint user-level JWTs.

  4. Copy the key and store it somewhere safe.

    The Create API Key dialog showing the generated orgkey value and the copy button highlighted

Cognitive3D shows the key once and cannot retrieve it after you close the dialog. If you lose it, generate a new one.

Generate a user-level JWT

Your backend exchanges the organization API key for a participant-scoped JWT. Send a POST request to the srEmbedTokens endpoint for your project:

https://api.cognitive3d.com/v0/projects/YOUR_PROJECT_ID/srEmbedTokens

The request body names the participant the token is scoped to. expireInMinutes is optional and defaults to 360 minutes, with a maximum of 720 minutes:

{
  "participantId": "YOUR_PARTICIPANT_ID",
  "expireInMinutes": 720
}

Pass the organization API key in the Authorization header. This example mints a 12-hour token for project 5065:

curl -i --location 'https://api.cognitive3d.com/v0/projects/5065/srEmbedTokens' \
  --header 'Authorization: YOUR_ORGANIZATION_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"participantId": "YOUR_PARTICIPANT_ID", "expireInMinutes": 720}'

The response returns the token and its expiry as a Unix timestamp in milliseconds:

{
  "token": "YOUR_USER_LEVEL_JWT",
  "expiresAt": 1786611603826
}

Decoding the token shows the claims that bound it. The viewer refuses anything outside these scene IDs, version IDs, and project ID:

{
  "organizationId": 1,
  "participantId": "123456789012345678",
  "versionIds": [
    1234,
    1235,
    1236
  ],
  "sceneIds": [
    "p0123-hsajdf83-hshdjkf9-sdfasdfsaf-sfdsdfs",
    "p0123-sadfsfsf-dfdgdfgsdf-dsfgdsgdfg-dfgdf",
    "p0123-asdfsdff-sdfsdfsdf-sdfsdfsdfsdd-sfdd"
  ],
  "exp": 1786586702,
  "projectId": 0123,
  "iat": 1786565102
}

Note

This release does not cover multiplayer sessions. A participant-scoped token reaches one participant's sessions only.

Pick the target session

To set up Embeddable Session Replay on your own website you need five pieces of data: Project ID, Scene ID, Scene Version Number, Session ID, and the user-level JWT from the previous step. The fastest way to collect them is to start from a session you already have open.

Open the session in Session Replay on the Dashboard. Select Advanced in the top bar. Press Open in Embed under Developer. This carries the Project ID, Scene ID, Scene Version Number, and Session ID into the Embed Configurator for you.

The Session Replay Advanced menu expanded, showing the Open in Embed button under the Developer section

If you do not have the session open already, select it manually in the Embed Configurator instead.

Select a session in the Embed Configurator

Open the Embed Configurator to pick a session and preview the embed before you paste any code.

  1. Enter your organization API key under Authentication, then press Verify Write Permission.

    The Authentication panel of the Embed Configurator with the Organization Key field filled in and a Verify Write Permission button below it

    The configurator needs a write-permission key because it mints the JWT for you. In production, the key stays on your backend.

  2. Select your Project, Scene, and Scene Version from the dropdown lists.

    The Embed Configurator project selector showing Organization ID, Project, Scene, and Scene Version dropdown lists

    The Organization ID above the dropdowns confirms which organization the key belongs to.

  3. Choose the Session Type, then pick the session you want to replay. A scene session targets a single scene, while a project session spans scene visits across a project.

    The Embed Configurator session picker showing the Scene Session and Project Session toggle, a paginated session list, and the internal session hash field below it

    Picking a session fills in the internal session hash under Session IDs. You can also paste a hash directly.

  4. Change the rendering and camera settings to match your scenario.

    The Rendering and Camera panels of the Embed Configurator, with Camera Controls set to Top Down and an Elevation Clip slider at 66 percent

    Camera Controls maps to controlType and Elevation Clip maps to verticalSliceAmount in the generated config.

  5. Scroll to the bottom and press Reload Preview to confirm the embed works in the preview pane on the right.

    Three example code buttons in the Embed Configurator below a Reload Preview button: displaying iframe, generating JWT, and updating Rendering settings

    The three example code buttons generate an iframe snippet, a JWT snippet, and a rendering settings snippet. The generated code stays in sync with the rendering and camera settings you chose.

Embed Session Replay

  1. Paste the iframe example code into your frontend's HTML.
  2. Follow the instructions in the Example Code for generating JWT modal to wire up the token refresh flow. Paste the result into your JavaScript.
  3. Follow the instructions in the Example Code for updating Rendering settings modal to change the embed's display from your own dashboard at runtime.
  4. Open your website in a browser to confirm the embed loads and plays.

URL query parameters

The URL drives the viewer entirely. The path selects what to replay, and a single percent-encoded config JSON object carries authentication plus every render, camera, path, and aggregation setting. After load, the host page keeps talking to the viewer through postMessage, so settings, room data, and timeline position stay controllable at runtime.

The scene form targets one scene, and the project form spans scene visits across a project:

https://replay.cognitive3d.com/scene/{sceneId}?apiTokenMode=true&projectid={projectId}&version={version}&sessionIds={json}&sessionId={numeric}&config={json}

https://replay.cognitive3d.com/project/{projectId}?apiTokenMode=true&sessionIds={json}&config={json}

Three rules apply to every URL:

  • sessionIds and config are JSON. Always run them through encodeURIComponent rather than hand-substituting %7B and %22.
  • apiKey is not a top-level query parameter. It lives inside config.
  • Query keys are case-sensitive exactly as written below.

Top-level query parameters

Field Type Default Definition
projectId numeric string -1 (falls back to the scene's project) Numeric Cognitive3D project ID. Required in the scene form. In the project form it comes from the path instead.
version integer string -1 Scene version number, the human-facing v17. Ignored in project mode, where visits carry their own versions.
versionId integer string -1 Internal scene version ID, a database ID rather than the version number. Scene Viewer links use this. When present it wins over the session's own version.
sceneId UUID string from path Overrides the scene UUID taken from /scene/{sceneId}.
sessionIds URL-encoded JSON array of strings [] Internal session hash IDs to load, for example ["1775515259_5b5bf774..."]. Multiple IDs load a multi-session replay. Read once at page load. Later selection changes rewrite the URL but do not re-seed from it.
selectedSessionId string none The viewer writes this when more than one session is active, so a shared link reopens with the same session focused. Removed automatically when only one session is active.
sessionId numeric string -1 Numeric session ID used to deep-link the player for audio, transcript, and project-visit lookups. Not consumed in project mode.
config URL-encoded JSON object {} The viewer configuration override, deep-merged over the app defaults. Carries apiKey and every setting below. Invalid JSON is ignored with a console warning.
apiTokenMode true or false false Token-authenticated embed mode. The Embed Configurator always sets true. In this mode the viewer skips the CSRF and cookie login path, gates rendering on a token being present, and asks the parent for one with tokenRequired when the URL carries none.
mode sessionReplay, sceneViewer, objectExplorer, or appWidget sessionReplay, or sceneViewer on /viewer/model Viewer mode. Each mode picks its own default camera control and aggregation type.
debug true or false false Enables the Tweakpane debug UI.

The config object

The config object is deep-merged over the viewer's defaults, so include only what you want to change. The Embed Configurator omits every field that already matches the default.

Field Type Default Definition
apiKey string "" The credential the viewer authenticates with: a user-level embed JWT sent as the c3d-sr-embed-token header, or an organization API key sent as Authorization. A JWT scopes access by its sceneIds, versionIds, and projectId claims, and anything outside those claims is refused.
controlType freeLook, thirdPerson, firstPerson, orbit, topdown, or fps freeLook in Session Replay Camera mode. freeLook orbits freely and frames the participant, thirdPerson follows behind the HMD, firstPerson rides the HMD, and orbit, topdown, and fps are the static-scene controls.
verticalSliceAmount number 0 to 1 1 Elevation clip. 1 shows the whole scene. Lower values clip geometry above a Y plane so you can see into a building from above.
cameraSpeed number in meters per second 2 Scales camera zoom, pan, and keyboard movement.
renderConfig object see below Render and UI toggles.
pointCloudConfig object see below Cube point-cloud aggregation appearance.
heatmapConfig object see below Aggregated heatmap appearance.
slicerQueryData object undefined Pre-computed aggregation payload, { main: { bin_size, session_count, counts[] } }, injected directly instead of querying the API.
sceneConfig object see below scale, sceneName, sdkVersion, cameraPos, and cameraRot. The viewer mostly writes these back.

Render configuration

renderConfig controls what the viewer draws and which parts of its own UI appear.

Field Type Default Definition
ui boolean true Show the player UI: timeline, header, and settings. Set false for a bare canvas.
autoplay boolean false Start playback on load. With audio present, the browser may require a consent click first. Project mode defers autoplay to the audio path.
loopSession boolean true Restart from the beginning when the timeline ends.
darkMode boolean false Dark background for the 3D scene itself.
darkModeForWidgets boolean false Dark theme for the surrounding UI chrome. Sets data-theme on the document.
gridEnabled boolean false Floor grid.
axesHelperEnabled boolean false Debug XYZ axes at the origin.
boundingBoxEnabled boolean false Debug bounding boxes around scene geometry and Dynamic Objects.
boundaryEnabled boolean true VR play space boundary polygon.
legendEnabled boolean true Color-map legend for the active aggregation display.
eventToastEnabled boolean false Toast popups when session events fire during playback.
controllerModalEnabled boolean true Controller-button overlay showing which inputs are pressed.
fullscreenButtonEnabled boolean false in Session Replay Floating fullscreen button, for embeds running with ui: false.
fullscreen boolean false Enter or exit fullscreen. Intended to be toggled at runtime with postMessage.
sceneVersionEnabled boolean true Show the Scene Version label.
pathVisible boolean false Master toggle for the movement path overlay. pathType and pathSize do nothing while this is false.
pathType head or floor head Draw the HMD trajectory or its floor projection.
pathSize number 0 to 1 0.1 Path line weight. The configurator's slider shows a percentage and stores the value divided by 100.
cloudPointsEnabled boolean true Render the cube point-cloud aggregation.
cloudPointsSize number 0 to 10 1 in Session Replay, 25 in Object Explorer Point-cloud cube size.
pointsAlwaysOnTop boolean false Draw aggregation points over scene geometry. The app widget uses this.
heatmapEnabled boolean false Render the aggregated heatmap volume instead of cubes.
ssaoEnabled boolean true Screen-space ambient occlusion post-process, labeled Ambient Occlusion in the UI.
ssaoRadius and ssaoStrength number 0.1 and 0.5 SSAO sampling radius in view-space units, and darkening strength.
sobelEnabled and sobelIntensity boolean and number false and 1.0 Sobel edge-darkening post-process and its multiplier.
sceneVisible boolean true Hide the scene mesh while keeping session data visible.
distanceCulling { enabled, maxDistance } { true, 300 } Skip geometry beyond maxDistance meters from the camera.
maxFPS number 120 Frame-rate cap.
resolutionFactor number 1 Render-scale multiplier. Below 1 trades sharpness for performance.
sliceClipY number computed Absolute Y clip plane derived from verticalSliceAmount. Set the percentage instead.
runQueryTimestamp number undefined Bump to a fresh Date.now() to force the aggregation query to re-run on the next config update.

Point cloud and heatmap appearance

pointCloudConfig and heatmapConfig share the same shape. Two fields apply to the point cloud only.

Field Type Default Definition
colorMap default, inferno, jet, plasma, viridis, magma, cividis, or turbo default Color palette used for density.
normalizationMethod minmax, percentile, logPercentile, log, or cdf minmax How raw counts map into the 0 to 1 color range. The percentile modes are the ones that read percentileParams.
gammaCorrection number 0 to 1 0.55 Intensity gamma. Higher values brighten low-density areas.
percentileParams.min and .max number 0 to 1 0.01 and 0.99 for the point cloud, 0 and 0.99 for the heatmap Lower and upper percentile clamps applied before normalization.
defaultPointSize number 2 in Session Replay Minimum point size used as the normalization floor.
blendMode 1 blend, 2 additive, or 3 opaque 1 Point-cloud blending. Point cloud only.
tint [r, g, b] [1, 1, 1] Color multiplier. Point cloud only.
active and enabled boolean true and false Whether the point cloud or heatmap subsystem is live.

Configuration example

This configuration hides the UI, starts playback on load, uses the third-person camera, and draws the head path at 25 percent weight:

{
  "apiKey": "YOUR_USER_LEVEL_JWT",
  "controlType": "thirdPerson",
  "renderConfig": {
    "ui": false,
    "autoplay": true,
    "pathVisible": true,
    "pathType": "head",
    "pathSize": 0.25
  }
}

Runtime postMessage API

Once the embed is running, the parent page and the iframe exchange messages over the postMessage Web API. Always post to the viewer's exact origin, never '*', and check both event.source and event.origin on every message you receive.

Messages from the parent page to the iframe

Message Effect
{ config: {…} } Apply a viewer config, using the same shape as the URL config. A message whose only key is apiKey is treated as a pure token update and merges into the current config instead of replacing it.
{ roomData: {…} } Load MR Room Layout anchors: { roomManifest: [{ id, label, anchors: [{ id, label, shape: "plane" or "volume" }] }], roomData: [{ id, time, enabled, p:[x,y,z], r:[x,y,z,w], s:[…] }] }. Send { roomData: null } to clear.
{ jumpToTimestamp: 12345 } Scrub playback to that timeline position, in milliseconds.

Messages from the iframe to the parent page

Message Meaning
{ iframeReady: true } The viewer is initialized and accepting config messages.
{ clientConfig: {…} } The resolved config after init, echoing back defaults the parent did not set.
{ eventList: [{ name, time, uid }], sensorList: [{ name, category, label }] } Sent once the session's events, and sensors if any, are loaded. Use it to build your own timeline or event menu.
{ tokenRequired: true } apiTokenMode is on and no credential arrived in the URL. Push one now, because boot is blocked until you do.
{ tokenRefreshRequired: true, status: 401 } The token expired or was rejected. Mint a fresh one and push it. The viewer holds requests briefly and ignores the rejected token.
{ roomError: "…" } The room payload was rejected, with the reason.
{ viewerError: { error } } Initialization failed.

Handling token initialization and refresh

This snippet boots the iframe, answers the tokenRequired handshake, and logs a tokenRefreshRequired prompt. In production, replace the hard-coded values with a token your backend mints per customer at runtime, and with the embed URL from the Embed Configurator:

(() => {
  // In production, set the JWT from your parent app's own API response: your
  // backend mints a short-lived, per-customer token and hands it to the page at
  // runtime. A token hard-coded into page source is served to every visitor.
  const JWT = 'YOUR_USER_LEVEL_JWT';
  const EMBED_URL = 'YOUR_EMBED_URL';

  const u = new URL(EMBED_URL, location.href);
  u.searchParams.set('apiTokenMode', 'true');
  const cfg = u.searchParams.get('config') ? JSON.parse(u.searchParams.get('config')) : {};
  delete cfg.apiKey;                                    // token stays in memory
  u.searchParams.set('config', JSON.stringify(cfg));
  const origin = u.origin;                              // the viewer's exact origin

  const existing = [...document.querySelectorAll('iframe')].find((f) => /replay|cognitive3d/i.test(f.src || ''));
  const frame = existing ?? Object.assign(document.body.appendChild(document.createElement('iframe')), {
    style: 'position:fixed;inset:0;width:100vw;height:100vh;border:0;z-index:2147483647;background:#000',
  });

  let current = null;

  // Never logs the token itself.
  const push = (t = JWT) => {
    const jwt = String(t || '').trim();
    if (!jwt) return console.error('[SR] no token to push');
    if (jwt === current) console.warn('[SR] same token re-pushed, the viewer ignores this while a refresh is pending');
    frame.contentWindow.postMessage({ config: { apiKey: jwt } }, origin);
    current = jwt;
    console.log('[SR] -> { config: { apiKey } }');
  };

  const onMessage = (e) => {
    if (e.source !== frame.contentWindow || e.origin !== origin) return;   // both checks required
    const d = e.data;
    if (!d || typeof d !== 'object') return;
    if ('tokenRequired' in d) { console.log('[SR] <- tokenRequired'); push(); }
    else if ('iframeReady' in d) { console.log('[SR] <- iframeReady'); if (!current) push(); }
    else if ('tokenRefreshRequired' in d) {
      // The token expired. Request a fresh one from your backend and push it.
      // The viewer holds its requests for 30 seconds and ignores the rejected token.
      console.warn('[SR] <- tokenRefreshRequired', d.status, 'mint a new token and call SR.push(freshJwt)');
    }
    else if ('clientConfig' in d) console.log('[SR] <- clientConfig versionId=', d.clientConfig?.versionId);
    else if ('viewerError' in d) console.error('[SR] <- viewerError', d.viewerError?.error);
  };
  window.removeEventListener('message', window.__srOnMessage);
  window.addEventListener('message', (window.__srOnMessage = onMessage));

  const reload = () => {
    current = null;
    const s = u.toString();
    frame.src = 'about:blank';
    requestAnimationFrame(() => (frame.src = s));
  };
  window.SR = { push, reload, frame, url: u.toString() };   // debug handle, drop in production
  console.log('[SR] ready, waiting for tokenRequired. SR.push(freshJwt) / SR.reload()');
  reload();
})();

Updating rendering settings at runtime

The viewer applies each { config } message as a full replacement rather than a merge, so the parent page must keep a live copy of the config and repost the complete object with only the fields it is changing. This snippet keeps that copy in sync from the viewer's own clientConfig messages and exposes three helpers:

(() => {
  // Keep your own copy of the config in sync rather than baking a literal in
  // at generation time. The same caveat applies to the credential below as to
  // the hard-coded token in the previous snippet.
  let currentConfig = {
    graphicsAPI: 'webgl',
    viewerMode: 'sessionReplay',
    controlType: 'thirdPerson',
    controlSettings: {
      topdown: { moveSpeed: 0.5, zoomSpeed: 1.1, dragSpeed: 1, blockX: false, blockZ: false },
      fps: { moveSpeed: 0.1, lookSpeed: 0.002 },
      orbit: { rotationSpeed: 0.005, zoomSpeed: 0.5, panSpeedMultiplier: 0.001, minRadius: 0.001, maxRadius: 1000 },
    },
    apiKey: 'YOUR_USER_LEVEL_JWT',
    renderConfig: {
      ui: true,
      autoplay: true,
      loopSession: true,
      distanceCulling: { enabled: true, maxDistance: 300 },
      maxFPS: 120,
      resolutionFactor: 1,
      darkMode: false,
      darkModeForWidgets: true,
      sceneVisible: true,
      cloudPointsEnabled: true,
      heatmapEnabled: false,
      legendEnabled: true,
      boundingBoxEnabled: false,
      eventToastEnabled: true,
      boundaryEnabled: true,
      controllerModalEnabled: true,
      fullscreenButtonEnabled: false,
      pathVisible: false,
      pathType: 'head',
      pathSize: 0.1,
      axesHelperEnabled: false,
      gridEnabled: false,
      ssaoEnabled: true,
      elevationScaleEnabled: true,
      cloudPointsSize: 1,
      pointsAlwaysOnTop: false,
    },
    dynamicObjects: [],
    pointCloudConfig: {
      percentileParams: { min: 0, max: 0.99 },
      gammaCorrection: 0.4,
      defaultPointSize: 2,
      colorMap: 'default',
      blendMode: 1,
      tint: [1, 1, 1],
      active: true,
      normalizationMethod: 'minmax',
    },
    heatmapConfig: {
      colorMap: 'default',
      enabled: false,
      normalizationMethod: 'minmax',
      percentileParams: { min: 0, max: 0.99 },
      gammaCorrection: 0.4,
    },
    captureScreen: { enabled: false },
    sceneConfig: { scale: 1, sceneName: '', sdkVersion: '', cameraPos: [] },
    verticalSliceAmount: 1,
    iframeReady: true,
  };

  const frame = [...document.querySelectorAll('iframe')].find((f) => /replay|cognitive3d/i.test(f.src || ''));
  if (!frame) console.error('[SR] no Session Replay iframe found on this page');
  const origin = frame ? new URL(frame.src, location.href).origin : null;

  // Stay in sync with whatever the viewer reports back, so a later call here
  // never clobbers a change made some other way.
  const onMessage = (e) => {
    if (!frame || e.source !== frame.contentWindow || e.origin !== origin) return;
    if (e.data && e.data.clientConfig) currentConfig = e.data.clientConfig;
  };
  window.removeEventListener('message', window.__srRenderConfigOnMessage);
  window.addEventListener('message', (window.__srRenderConfigOnMessage = onMessage));

  const post = (patch) => {
    if (!frame) return console.error('[SR] no iframe to post to');
    currentConfig = { ...currentConfig, ...patch };
    frame.contentWindow.postMessage({ config: currentConfig }, origin);
    console.log('[SR] -> config update', patch);
  };

  // Show or hide the built-in UI controls: header, menus, and timeline chrome.
  window.SR_setUIControls = (show) =>
    post({ renderConfig: { ...currentConfig.renderConfig, ui: !!show } });

  // Switch the camera control mode. Values are listed under controlType above.
  window.SR_setCameraControlType = (controlType) => post({ controlType });

  // Set the elevation clip amount, where 1 is 100 percent and clips nothing.
  window.SR_setElevationClip = (amount) =>
    post({ verticalSliceAmount: Math.min(1, Math.max(0, amount)) });

  console.log('[SR] ready, try SR_setUIControls(false), SR_setCameraControlType("firstPerson"), or SR_setElevationClip(0.68)');
})();

Call the helpers from your own UI to change the embed's display without reloading it:

// Switch the camera to the first-person perspective.
SR_setCameraControlType('firstPerson');

// Set the elevation clip to a value between 0 and 1.
SR_setElevationClip(0.68);

See also

  • Session Replay — the full replay experience on the Cognitive3D Dashboard.
  • Scene Viewer — inspect an uploaded scene mesh without session data.

intercom If you have a question or any feedback about our documentation please use the Intercom button (purple circle) in the lower right corner of any web page or join our Discord.