Shopify 3D 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
- Log in to your Shopify admin portal at https://admin.shopify.com/
- Select your store
- Navigate to
Online Store
->Themes
in the left menu - Locate your current theme and click on
Actions
(three dots) ->Duplicate
- This creates a backup of your theme in case you need to revert changes
Step 2: Access Theme Code
- Find your duplicated theme
- 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.
- In the
Assets
folder, locateproduct-model.js
- Verify that it contains the following code.
- If you can't find this file, search for similar code in other JavaScript files
product-model.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
- Create a new file in the
Assets
folder namedproduct-model-ijewel.js
- Copy the following code into this new file.
- Save the file
product-model-ijewel.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;
},
};
- 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.
- 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
- In each file, find the code block that loads the code. It should look similar to this:
{%- 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 -%}
- Replace the filename in the above code with
product-model-ijewel.js
and optionally get the settings from metafields and set asiJewelSDKSettings
global variable:
{%- 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.
- 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.
- Go to
Settings
->Metafields
in your Shopify admin - Create a new metafield for your shop with the namespace
ijewel3d_settings
. (or pick your own) - Add your desired settings as a JSON object. (or individual)
- Save the metafield
- Update the code in the theme to read the meta-field and set the
iJewelSDKSettings
global variable. (see Step 5.3)
Example settings:
{
"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
- Edit a product in your Shopify admin
- In the Media section, click
Add media
->Add 3D model
- Upload a GLB or GLTF file
- Test model: Download Test Model
- 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
- Preview the product page with the 3D model and the theme.
- Verify that the iJewel viewer loads correctly and functions as expected
- 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],