Skip to content

View the maintained live template and its source.

iframe Host Page: Complete Example

A host page with controls that communicate with the iframe viewer via postMessage.

Back to Embedding guide

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Our Wedding Bands</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #f5f5f5; }

    .page { max-width: 1200px; margin: 0 auto; padding: 24px; }
    h1 { font-size: 24px; margin-bottom: 16px; }

    .viewer-container {
      width: 100%;
      height: 70vh;
      border-radius: 12px;
      overflow: hidden;
      background: #fff;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    }
    .viewer-container iframe { width: 100%; height: 100%; border: none; }

    .controls { margin-top: 24px; display: flex; gap: 12px; flex-wrap: wrap; }
    .controls button {
      padding: 8px 20px;
      border: 1px solid #ddd;
      border-radius: 8px;
      background: #fff;
      cursor: pointer;
      font-size: 14px;
    }
    .controls button:hover { background: #f0f0f0; }

    .price-display {
      margin-top: 24px;
      font-size: 28px;
      font-weight: 600;
    }
  </style>
</head>
<body>
  <div class="page">
    <h1>Design Your Wedding Band</h1>

    <div class="viewer-container">
      <iframe
        id="wbb"
        src="https://your-site.com/wbb-viewer.html"
        allow="camera; xr-spatial-tracking"
      ></iframe>
    </div>

    <div class="controls">
      <button onclick="send('setProfile', [0])">D-Shape</button>
      <button onclick="send('setProfile', [1])">Flat</button>
      <button onclick="send('setProfile', [2])">Concave</button>
      <button onclick="send('setMaterial', [1, 'White', 'Polished'])">White Gold</button>
      <button onclick="send('setMaterial', [1, 'Yellow', 'Polished'])">Yellow Gold</button>
      <button onclick="send('setMaterial', [1, 'Rose', 'Polished'])">Rose Gold</button>
      <button onclick="send('switchBand', ['her'])">Her Ring</button>
      <button onclick="send('switchBand', ['his'])">His Ring</button>
    </div>

    <div class="price-display" id="price">Loading...</div>
  </div>

  <script>
    const iframe = document.getElementById('wbb');
    const viewerOrigin = new URL(iframe.src).origin;
    let reqId = 0;
    let connected = false;
    const pending = new Map();

    function send(method, args = []) {
      iframe.contentWindow.postMessage({
        id: `req-${++reqId}`, method, args,
      }, viewerOrigin);
    }

    function query(method, args = [], timeoutMs = 8000) {
      return new Promise((resolve, reject) => {
        const id = `req-${++reqId}`;
        const timer = setTimeout(() => {
          pending.delete(id);
          reject(new Error(`Timed out waiting for ${method}`));
        }, timeoutMs);

        pending.set(id, { resolve, reject, timer });
        iframe.contentWindow.postMessage({ id, method, args }, viewerOrigin);
      });
    }

    async function markConnected() {
      if (connected) return;
      connected = true;
      document.getElementById('price').textContent = 'Ready';

      try {
        const snapshot = await query('getSnapshot');
        if (snapshot?.pricing) {
          document.getElementById('price').textContent =
            `$${snapshot.pricing.totalUsd.toFixed(2)}`;
        }
      } catch (error) {
        console.error('Initial Wedding Band snapshot failed', error);
      }
    }

    window.addEventListener('message', (e) => {
      if (e.source !== iframe.contentWindow) return;
      if (e.origin !== viewerOrigin) return;

      const data = e.data;
      if (!data || typeof data !== 'object') return;

      if (data.id && pending.has(data.id)) {
        const request = pending.get(data.id);
        pending.delete(data.id);
        clearTimeout(request.timer);
        if (data.error) request.reject(new Error(data.error.message));
        else request.resolve(data.result);
        return;
      }

      if (data.event === 'ready') void markConnected();

      // Update price display on price events
      if (data.event === 'price:updated' && data.data?.pricing) {
        document.getElementById('price').textContent =
          `$${data.data.pricing.totalUsd.toFixed(2)}`;
      }
    });

  </script>
</body>
</html>