Skip to content

Shopify 3D Viewer integration

Shopify Viewer Integration

This guide outlines the process of integrating the iJewel 3D Viewer into Shopify using Dawn or related themes. The iJewel 3D Viewer replaces the default Google/Shopify Model Viewer on the Product Display Page(PDP), offering high-quality rendering for jewelry and fashion items, and additional features like custom configurators, post-processing effects, AR TryOn etc

Prerequisites

  • Access to your Shopify admin portal
  • Basic understanding of Shopify theme structure
  • Basic Familiarity with HTML, CSS, and JavaScript
  • iJewel3D SDK Website License. Check out the license page for more details.

TIP

If you are looking for a development team for the = integration, check our Development Partners

Step 1: Backup Your Theme

  1. Log in to your Shopify admin portal at https://admin.shopify.com/
  2. Select your store
  3. Navigate to Online Store -> Themes in the left menu
  4. Locate your current theme and click on Actions (three dots) -> Duplicate
  5. This creates a backup of your theme in case you need to revert changes

Step 2: Access Theme Code

  1. Find your duplicated theme
  2. Click on Actions -> Edit code. This would open the Code Editor with the theme.

Step 3: Verify Existing Product Model Code

Themes include a file product-model.js that loads the default Google Model Viewer. We need to check that it exists and replace this code with the iJewel 3D Viewer.

  1. In the Assets folder, locate product-model.js
  2. Verify that it contains the following code.
  3. If you can't find this file, search for similar code in other JavaScript files
product-model.js
js
if (!customElements.get('product-model')) {
  customElements.define(
    'product-model',
    class ProductModel extends DeferredMedia {
      constructor() {
        super();
      }

      loadContent() {
        super.loadContent();

        Shopify.loadFeatures([
          {
            name: 'model-viewer-ui',
            version: '1.0',
            onLoad: this.setupModelViewerUI.bind(this),
          },
        ]);
      }

      setupModelViewerUI(errors) {
        if (errors) return;

        this.modelViewerUI = new Shopify.ModelViewerUI(this.querySelector('model-viewer'));
      }
    }
  );
}

window.ProductModel = {
  loadShopifyXR() {
    Shopify.loadFeatures([
      {
        name: 'shopify-xr',
        version: '1.0',
        onLoad: this.setupShopifyXR.bind(this),
      },
    ]);
  },

  setupShopifyXR(errors) {
    if (errors) return;

    if (!window.ShopifyXR) {
      document.addEventListener('shopify_xr_initialized', () => this.setupShopifyXR());
      return;
    }

    document.querySelectorAll('[id^="ProductJSON-"]').forEach((modelJSON) => {
      window.ShopifyXR.addModels(JSON.parse(modelJSON.textContent));
      modelJSON.remove();
    });
    window.ShopifyXR.setupXRElements();
  },
};

window.addEventListener('DOMContentLoaded', () => {
  if (window.ProductModel) window.ProductModel.loadShopifyXR();
});

Step 4: Add iJewel Viewer Code

  1. Create a new file in the Assets folder named product-model-ijewel.js
  2. Copy the following code into this new file.
  3. Save the file
product-model-ijewel.js
js
/**
 * This file is an alternative of `product-model.js` on Shopify.
 * It overloads the default model-viewer with the `webgi-viewer` from the iJewel3D SDK(https://ijewel3d.com).
 * This file must be included in the theme manually, and referenced in the pages where product-model.js was used.
 * Check the docs for more information and updates. - https://developer.ijewel3d.com/integrations/shopify
 *
 * File version - 16-09-2024
 */

// Settings for the iJewel3D SDK, this includes the webgi package version, environment maps etc.
// This can be set externally in the metafields or in the page where the product model is used, if not, the default settings will be used.
window.iJewelSDKSettings = {
  webgi_version: "0.9.17",
  scene_settings: "",
  environment_map: "",
  license_key: "",
  loading: {
    loadingTextHeader: "Loading iJewel 3D Experience",
    showFileNames: false,
    backgroundOpacity: 0.5,
    background: "#ffffff",
    textColor: "#222222",
    logoImage: "https://playground.ijewel3d.com/logo_black.png",
  },
  ...(window.iJewelSDKSettings||{}),
}

// Create a custom model-viewer element that extends the HTMLElement, this will be used to load the iJewel3D SDK viewer.
// Note that it has to be made sure that this should be run before loading the google model viewer, since we cannot redefine an html element.
if(!customElements.get('model-viewer')){
  const viewerScript = document.createElement('script');
  viewerScript.setAttribute('src',
    window.iJewelSDKSettings.webgi_version.includes('/') ? window.iJewelSDKSettings.webgi_version : // assume URL
      'https://dist.pixotronics.com/webgi/runtime/viewer-'+(window.iJewelSDKSettings.webgi_version||'0.9.17')+'.js'
  );
  document.head.appendChild(viewerScript);
  //todo: fix for proper multiple viewer support.
  let globalViewer = null; // to make sure we use only 1 viewer and canvas on a page,
  customElements.define('model-viewer', class ModelViewerWebGi extends HTMLElement{
    constructor() {
      super();
      const container = document.createElement('div')
      this.container = container
      this.container.style.width = "100%"
      this.container.style.height = "100%"
      this.appendChild(container)

      this.modelLoaded = false;

      this.initialize()
    }


    doOnLoad(fn){
      if(this.modelLoaded) fn()
      this.onLoad = fn;
    }

    async initialize(){
      if(globalViewer) {
        if(globalViewer === 1) return
        this.viewer = globalViewer
        this.viewer.scene.removeSceneModels()
      }else {
        globalViewer = 1;
        this.viewer = new CoreViewerApp({container: this.container})
        await this.viewer.initialize({caching: true, ground: true, bloom: true, enableDrop: false, importPopup: true, debug: false})
        this.viewer.getPluginByType('Diamond').setKey(window.iJewelSDKSettings.license_key)
        
        // ENCRYPTED_MODELS
        // Uncomment this and set your password to load encrypted models.
        // viewer.getManager().importer.registerFile("temp.glb").preparsers[0].key = (encryption, json)=> {
        //   // console.log(encryption, json);
        //   return "password";
        // };
        
        globalViewer = this.viewer;
        this.viewer.canvas.style.width = "100%"
        this.viewer.canvas.style.height = "100%"
      }
      const src = this.getAttribute('src')
      let envMapPromise = null
      this.refreshLoadingScreen()
      if(window.iJewelSDKSettings.environment_map && window.iJewelSDKSettings.environment_map.length){
        envMapPromise = this.viewer.getManager().importer.importSinglePath(window.iJewelSDKSettings.environment_map)
      }
      const modelPromise = this.viewer.load(src)
      const [model, envMap] = await Promise.all([modelPromise, envMapPromise])
      this.refreshLoadingScreen()
      if(window.iJewelSDKSettings.scene_settings && window.iJewelSDKSettings.scene_settings.length){
        await this.viewer.load(window.iJewelSDKSettings.scene_settings)
      }
      this.refreshLoadingScreen()
      if(envMap) this.viewer.scene.setEnvironment(envMap)
      this.modelLoaded = true

      // await timeout(200)
      setTimeout(()=>{
        if(this.onLoad) this.onLoad()
        window.dispatchEvent(new CustomEvent('ijewel-viewer-loaded', {detail: this}))
      }, 200)

    }

    refreshLoadingScreen(){
      const loadingSettings = window.iJewelSDKSettings.loading ?? {}
      const loadingPlugin = this.viewer.getPluginByType('LoadingScreenPlugin')
      Object.entries(loadingSettings).forEach(([key, value])=>{
        loadingPlugin[key] = value
      })
    }

  })
}


// Create a custom ProductModel element that extends the DeferredMedia element, this disables loading of google-model-viewer
if (!customElements.get('product-model')) {
  customElements.define('product-model', class ProductModel extends DeferredMedia {
    constructor() {
      super();
      const template = this.querySelector('template')
      const viewer = template.content.querySelector('model-viewer')
      delete viewer.dataset.shopifyFeature // disables loading of google model viewer script by shopify
    }

    loadContent() {
      super.loadContent();

      const poster = this.querySelector('.deferred-media__poster')
      poster.style.display = 'block'
      poster.style.zIndex = 100;
      const posterButton = this.querySelector('.deferred-media__poster-button')
      posterButton.style.display = 'none'

      const modelViewer = this.querySelector('model-viewer')
      modelViewer.doOnLoad(()=>{
        poster.style.display = 'none'
      })
    }

    setupModelViewerUI(errors) {
      if (errors) return;
    }
  });
}

// Set a dummy ProductModel in the window, just in case
window.ProductModel = {
  loadShopifyXR() {
  },

  setupShopifyXR(errors) {
    if (errors) return;
  },
};
  1. Add the license key for your domain in the iJewelSDKSettings object(or in a metafield). This is required for the iJewel3D SDK to work correctly.
Loading Encrypted Models

Encryption can be used from the iJewel3D Playground to encrypt models, this protects your models from being downloaded and directly viewed in editors/viewers without a password.

To load encrypted models, uncomment the code block ENCRYPTED_MODELS in the initialize function and set the password in the key function. This will be used to decrypt the model before loading. The password can be based on the model/meta-field or a fixed global password.

Note that the password is added to the frontend javascript file. While this prevents non-technical users to easily extract the 3d file, experienced engineers can still extract it by reading the front-end code, and use it to decrypt and view the file. More strategies like obfuscation(and other techniques) can be used to further protect the password. This is very useful when using dynamic passwords for different models, since any bad actors would need to individually extract the password for each model.

Step 5: Update Theme Files

Next, we need to replace the reference of product-model.js with product-model-ijewel.js in the pages where the product model is used.

  1. Locate the files that reference the file, in the default theme(Dawn) this would be:
  • sections/main-product.liquid
  • sections/featured-product.liquid
  • Any other relevant section files
  1. In each file, find the code block that loads the code. It should look similar to this:
liquid
{%- if first_3d_model -%}
 <script type="application/json" id="ProductJSON-{{ product.id }}">
   {{ product.media | where: 'media_type', 'model' | json }}
 </script>
 <script src="{{ 'product-model.js' | asset_url }}" defer></script>
{%- endif -%}
  1. Replace the filename in the above code with product-model-ijewel.js and optionally get the settings from metafields and set as iJewelSDKSettings global variable:
liquid
{%- if first_3d_model -%}
 <script type="application/json" id="ProductJSON-{{ product.id }}">
   {{ product.media | where: 'media_type', 'model' | json }}
 </script>
 {%- if shop.metafields.ijewel3d_settings -%}
   <script>
     var iJewelSDKSettings = {{ shop.metafields.ijewel3d_settings | json }};
   </script>
 {%- endif -%}
 <script src="{{ 'product-model-ijewel.js' | asset_url }}" defer></script>
{%- endif -%}

INFO

Note that this is enclosed in if first_3d_model block, this should already be present in the theme, and is done so that the script is only loaded once and when there is a 3d model in the product.

WARNING

There is also a reference to product-modal.js which is different, dont change that.

  1. Save all modified files

Step 6: Configure iJewel Settings (Optional)

To customize the iJewel viewer, the settings can be changed directly in the code for the file product-model-ijewel.js.

If you would prefer to change them from the admin panel without editing the code, Shopify metafields can be added to store the settings, which are accessed in liquid as in Step 5.3 above.

  1. Go to Settings -> Metafields in your Shopify admin
  2. Create a new metafield for your shop with the namespace ijewel3d_settings. (or pick your own)
  3. Add your desired settings as a JSON object. (or individual)
  4. Save the metafield
  5. Update the code in the theme to read the meta-field and set the iJewelSDKSettings global variable. (see Step 5.3)

Example settings:

json
{
    "webgi_version": "0.9.17",
    "scene_settings": "",
    "environment_map": "",
    "loading": {
        "loadingTextHeader": "Loading iJewel 3D Experience",
        "showFileNames": false,
        "backgroundOpacity": 0.5,
        "background": "#ffffff",
        "textColor": "#222222",
        "logoImage": "https://playground.ijewel3d.com/logo_black.png"
    }
}

Step 7: Add 3D Models to Products

  1. Edit a product in your Shopify admin
  2. In the Media section, click Add media -> Add 3D model
  3. Upload a GLB or GLTF file
  1. Save the changes

Cover Image

Once the model is uploaded, shopify will automatically generate a preview image for the 3d model, but this can be replaced with a custom image by clicking on the model and editing the Cover Image. No other settings while editing the 3d model will be used from here.

Note

  • Currently, only one 3D model can be added per product.
  • If you see a configurator over the 3d model, it probably means the 3d model includes the data. To remove it, use the ijewel3d playground.

Step 8: Test the Integration

  1. Preview the product page with the 3D model and the theme.
  2. Verify that the iJewel viewer loads correctly and functions as expected
  3. Test on various devices and browsers to ensure compatibility

Troubleshooting

  • If the viewer doesn't appear, check the browser console for JavaScript errors
  • Ensure all file paths are correct in your theme files
  • Verify that the 3D model file is in a supported format (GLB or GLTF)

Next Steps

  • Explore additional iJewel SDK features and integrate them as needed
  • Check out other products at iJewel3D

Remember to thoroughly test your changes before pushing the updated theme live. Regularly check for updates to both the Shopify theme and the iJewel SDK to ensure compatibility and access to new features.

Contact

For any questions or assistance with the integration, or questions about the license, email us at [email protected],