Skip to content

Vanilla JavaScript: Complete Example

A plain HTML/JS setup with no frameworks. This is the simplest way to integrate the Wedding Band Builder.

Back to Script Tag guide

With Built-in UI

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Wedding Band Builder</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; }
    #viewer { width: 100vw; height: 100vh; }
  </style>
</head>
<body>
  <div id="viewer"></div>

  <script src="https://releases.ijewel3d.com/libs/mini-viewer/latest/bundle.iife.js"></script>
  <script>
    new ijewelViewer.Viewer(document.getElementById('viewer'), {
      name: 'Wedding Band Builder',
      version: 'v5',
      basePath: 'https://your-cdn.com/wbb-assets/',
      plugins: {
        WeddingBandBuilder: {
          manifestUrl: 'wedding-band-project.json',
          showUI: true,
        },
      },
    }, {
      showCard: false,
      showSwitchNode: false,
      showUiButtons: true,
      showConfigurator: false,
      showZoomButtons: true,
      enableZoom: true,
    });

    window.addEventListener('ijewel-viewer-ready', (e) => {
      const api = e.detail.viewer.getPluginByType('WeddingBandBuilder').controller;

      api.events.on('price:updated', (data) => {
        document.title = `Ring - $${data.pricing.totalUsd.toFixed(2)}`;
      });
    });
  </script>
</body>
</html>

Headless with Custom Controls

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Custom Wedding Band Configurator</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #fafafa; }
    .app { display: flex; flex-direction: column; height: 100vh; }
    #viewer { flex: 1; min-height: 300px; }

    .controls {
      background: #fff;
      border-top: 1px solid #e0e0e0;
      padding: 16px 24px;
    }
    .row { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; align-items: center; }
    .label { font-size: 12px; font-weight: 600; text-transform: uppercase; color: #888; width: 70px; }

    button {
      padding: 8px 16px;
      border: 1px solid #ddd;
      border-radius: 6px;
      background: #fff;
      cursor: pointer;
      font-size: 13px;
    }
    button:hover { border-color: #999; }
    button.active { background: #333; color: #fff; border-color: #333; }

    input[type="range"] { width: 180px; }
    .price-bar {
      padding: 16px 24px;
      background: #333;
      color: #fff;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .price { font-size: 24px; font-weight: 600; }
  </style>
</head>
<body>
  <div class="app">
    <div id="viewer"></div>

    <div class="controls">
      <div class="row">
        <span class="label">Profile</span>
        <div id="profiles"></div>
      </div>
      <div class="row">
        <span class="label">Metal</span>
        <div id="metals"></div>
      </div>
      <div class="row">
        <span class="label">Width</span>
        <input id="width" type="range" min="2" max="10" step="0.1" value="4" />
        <span id="width-val">4.0 mm</span>
      </div>
      <div class="row">
        <span class="label">Diamonds</span>
        <div id="diamonds"></div>
      </div>
    </div>

    <div class="price-bar">
      <div>
        <button id="btn-her" class="active" onclick="switchBand('her')" style="color:#fff;border-color:rgba(255,255,255,.3);background:rgba(255,255,255,.2)">Her</button>
        <button id="btn-his" onclick="switchBand('his')" style="color:#fff;border-color:rgba(255,255,255,.3);background:transparent">His</button>
      </div>
      <div class="price" id="price">-</div>
    </div>
  </div>

  <script src="https://releases.ijewel3d.com/libs/mini-viewer/latest/bundle.iife.js"></script>
  <script>
    let api;

    new ijewelViewer.Viewer(document.getElementById('viewer'), {
      name: 'Wedding Band Builder',
      version: 'v5',
      basePath: 'https://your-cdn.com/wbb-assets/',
      plugins: {
        WeddingBandBuilder: {
          manifestUrl: 'wedding-band-project.json',
          showUI: false,
        },
      },
    }, {
      showCard: false, showSwitchNode: false, showUiButtons: false,
      showConfigurator: false, showZoomButtons: false, enableZoom: true,
    });

    window.addEventListener('ijewel-viewer-ready', (e) => {
      api = e.detail.viewer.getPluginByType('WeddingBandBuilder').controller;

      // Profiles
      const profilesEl = document.getElementById('profiles');
      api.getAvailableProfiles().forEach((p, i) => {
        const btn = document.createElement('button');
        btn.textContent = p.name;
        if (i === 0) btn.classList.add('active');
        btn.onclick = async () => {
          await api.setProfile(p.index);
          profilesEl.querySelectorAll('button').forEach(b => b.classList.remove('active'));
          btn.classList.add('active');
        };
        profilesEl.appendChild(btn);
      });

      // Metals
      const metalsEl = document.getElementById('metals');
      api.getAvailableMetals().forEach((m) => {
        const btn = document.createElement('button');
        btn.textContent = m.name;
        btn.onclick = () => api.setMaterial(1, m.id, 'Polished');
        metalsEl.appendChild(btn);
      });

      // Diamonds
      const diamondsEl = document.getElementById('diamonds');
      const noneBtn = document.createElement('button');
      noneBtn.textContent = 'None';
      noneBtn.classList.add('active');
      noneBtn.onclick = () => {
        api.setDiamonds(null);
        diamondsEl.querySelectorAll('button').forEach(b => b.classList.remove('active'));
        noneBtn.classList.add('active');
      };
      diamondsEl.appendChild(noneBtn);

      api.getAvailableSettingTypes().filter(t => t !== 'none').forEach((t) => {
        const btn = document.createElement('button');
        btn.textContent = t;
        btn.onclick = () => {
          api.setDiamonds({ settingType: t });
          diamondsEl.querySelectorAll('button').forEach(b => b.classList.remove('active'));
          btn.classList.add('active');
        };
        diamondsEl.appendChild(btn);
      });

      // Width slider
      const widthSlider = document.getElementById('width');
      const widthVal = document.getElementById('width-val');
      const dims = api.getDimensions();
      widthSlider.value = dims.widthMm;
      widthVal.textContent = dims.widthMm.toFixed(1) + ' mm';

      widthSlider.addEventListener('input', (e) => {
        const mm = parseFloat(e.target.value);
        api.setWidth(mm);
        widthVal.textContent = mm.toFixed(1) + ' mm';
      });

      // Price
      api.events.on('price:updated', (data) => {
        if (data.pricing) {
          document.getElementById('price').textContent = '$' + data.pricing.totalUsd.toFixed(2);
        }
      });

      const price = api.getPrice();
      if (price) document.getElementById('price').textContent = '$' + price.totalUsd.toFixed(2);
    });

    function switchBand(name) {
      if (!api) return;
      api.switchBand(name);
      document.getElementById('btn-her').style.background = name === 'her' ? 'rgba(255,255,255,.2)' : 'transparent';
      document.getElementById('btn-his').style.background = name === 'his' ? 'rgba(255,255,255,.2)' : 'transparent';
    }
  </script>
</body>
</html>