Simplio3D

3D Viewer

Embed and control the 3D viewer programmatically. Configure camera, lighting, environment, manage viewer state, take screenshots, and respond to user interactions via callbacks.

Get Embed Configuration

GET/projects/:id/viewer/embed-config
const response = await fetch(BASE_URL + '/projects/proj_123/viewer/embed-config', {
  headers: { 'Authorization': 'Bearer ' + accessToken }
});

const data = await response.json();
// {
//   "success": true,
//   "config": {
//     "projectId": "proj_123",
//     "cameraPosition": [0, 2, 5],
//     "cameraTarget": [0, 0, 0],
//     "cameraFOV": 50,
//     "enableZoom": true,
//     "enablePan": true,
//     "enableAutoRotate": false,
//     "environmentPreset": "studio",
//     "lightIntensity": 1.0,
//     "ambientLightIntensity": 0.5,
//     "enableShadows": true,
//     "backgroundColor": "#f5f5f5",
//     "enableAR": false,
//     "loadingAnimation": "spinner",
//     "loadingMessage": "Loading 3D model..."
//   }
// }

Embed via iframe

The simplest way to embed a 3D viewer is via the share URL in an iframe.

<!-- Simple iframe embed -->
<iframe
  src="https://your-project.supabase.co/share/proj_123/SHARE_TOKEN"
  width="100%"
  height="600"
  frameborder="0"
  allow="xr-spatial-tracking"
  allowfullscreen
></iframe>

<!-- Embed with custom parameters -->
<iframe
  src="https://your-project.supabase.co/share/proj_123/SHARE_TOKEN?autoRotate=true&bg=%23ffffff"
  width="800"
  height="600"
  style="border: none; border-radius: 12px;"
></iframe>

Headless Viewer Integration

For fully custom UIs, use the headless API to build your own viewer with any 3D library (Three.js, Babylon.js, etc.).

// 1. Load project data with all option blocks and settings
const projectRes = await fetch(BASE_URL + '/projects/proj_123', {
  headers: { 'Authorization': 'Bearer ' + accessToken }
});
const { project } = await projectRes.json();

// 2. Load option blocks for the configurator UI
const blocksRes = await fetch(BASE_URL + '/projects/proj_123/option-blocks', {
  headers: { 'Authorization': 'Bearer ' + accessToken }
});
const { blocks } = await blocksRes.json();

// 3. Load pricing blocks for CPQ
const pricingRes = await fetch(BASE_URL + '/projects/proj_123/pricing-blocks', {
  headers: { 'Authorization': 'Bearer ' + accessToken }
});
const { blocks: pricingBlocks } = await pricingRes.json();

// 4. Build your custom UI with any framework
// - Render 3D scene using project.data.scene
// - Render option blocks as custom components
// - Apply material/visibility changes based on selections
// - Calculate prices in real-time

Get Viewer State

GET/projects/:id/viewer/state
const response = await fetch(BASE_URL + '/projects/proj_123/viewer/state', {
  headers: { 'Authorization': 'Bearer ' + accessToken }
});

const data = await response.json();
// {
//   "success": true,
//   "state": {
//     "loaded": true,
//     "selections": {
//       "dropdownSelections": { "blk_frame": "black" },
//       "selectMaterialSelections": { "blk_fabric": "linen" },
//       "checkboxSelections": { "blk_accessories": ["cup-holder"] },
//       "toggleSwitchSelections": { "blk_armrests": "with" },
//       "carouselSelections": {}
//     },
//     "visibleBlocks": ["blk_001", "blk_002", "blk_003"],
//     "currentPrice": { "totalPrice": 1019.99, "currency": "USD" },
//     "cameraPosition": [0, 2, 5],
//     "cameraTarget": [0, 0, 0]
//   }
// }

Take Screenshot

POST/projects/:id/viewer/screenshot
const response = await fetch(BASE_URL + '/projects/proj_123/viewer/screenshot', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ' + accessToken,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    width: 1920,
    height: 1080,
    format: 'png',
    cameraPosition: [2, 3, 5],
    cameraTarget: [0, 0.5, 0]
  })
});

const data = await response.json();
// { "success": true, "url": "https://...signed-screenshot-url..." }

PostMessage API (iframe communication)

When embedding via iframe, use postMessage for two-way communication.

// Send commands to the embedded viewer
const iframe = document.getElementById('simplio3d-viewer');

// Set a selection
iframe.contentWindow.postMessage({
  type: 'simplio3d:setSelection',
  blockId: 'blk_frame',
  value: 'silver'
}, '*');

// Set camera position
iframe.contentWindow.postMessage({
  type: 'simplio3d:setCamera',
  position: [3, 2, 4],
  target: [0, 0, 0]
}, '*');

// Request a screenshot
iframe.contentWindow.postMessage({
  type: 'simplio3d:screenshot',
  format: 'png'
}, '*');

// Listen for events from the viewer
window.addEventListener('message', (event) => {
  if (event.data.type === 'simplio3d:selectionChanged') {
    console.log('Selection changed:', event.data.selections);
  }
  if (event.data.type === 'simplio3d:priceChanged') {
    console.log('Price:', event.data.totalPrice, event.data.currency);
  }
  if (event.data.type === 'simplio3d:screenshotReady') {
    console.log('Screenshot URL:', event.data.url);
  }
  if (event.data.type === 'simplio3d:formSubmitted') {
    console.log('Form data:', event.data.formData);
  }
});

Continue reading