Skip to content

Developer FAQ ​

Short answers to the questions that developers ask most about the Viewer SDK and WebGi.

Short answers to the questions that developers ask most about the Viewer SDK and the WebGi engine.

How much does an SDK licence cost? ​

The SDK is licensed by domain. There is no licence key to buy or set.

  • Each production website needs one main domain. Main domains start on the Professional plan, and the organization plans include more. To get more, buy an Extra domain in the Extras card on Plan and billing. The pricing page shows the domains of each plan.
  • Test sites (testing, staging and client review) are free. localhost and *.local are always allowed.
  • www.brand.com and brand.com are two hostnames.
  • An agency that builds a site for a client is covered while it works for that client. Each production site of the client needs its own main domain, in the account of the agency or of the client.

For a quote for a large project, many domains or a partner request, contact us.

Which plan includes the SDK, the Viewer API or Try-On? ​

The pricing page compares what each plan includes. For the limits of each plan, see Plans and limits. To use the SDK on a production website, you need a main domain (see How much does an SDK licence cost?). For Try On on your own domain, see Try-On on your domain.

For an Enterprise plan or a custom agreement, contact us.

Do I need a paid licence to use the SDK? ​

  • On your computer: No. On localhost, 127.0.0.1 and 0.0.0.0, the SDK runs with no watermark and no console message.
  • On a public website: Yes. Register each hostname that loads the viewer. A hostname that is not registered shows the iJewel3D Evaluation Version mark, and the console shows this message:
text
A license is required to use iJewel3D SDK on custom websites.
  • An iframe embed from the iJewel platform needs no domain registration.

There is no licence key. The SDK checks the domain automatically. setKey() and diamondKey stay in the API, but they have no effect.

How do I register my domain and remove the watermark? ​

Step 1 of 5
  1. In the iJewel platform, open Settings → Apps, embed and API → Domains.
  2. Select Add domain. If you have no free production slot, select Buy domain first.
  3. Type only the hostname, for example www.example.com. Do not type https://, a port or a path.
  4. Add each hostname that loads the viewer. example.com, www.example.com and shop.example.com are different hostnames. Add your staging hostname too.
  5. Publish the page. Then open it in a private browser window.

A site that is not on the list shows the iJewel mark.

If the license call returns this error, the hostname is not on the list:

json
{"error":"No license key found for <host>"}

On an enterprise instance, you cannot add a domain yourself. Ask iJewel3D support to add it.

Refer to Register a domain for the SDK and The evaluation watermark shows.

How do I install WebGi and the Mini Viewer? ​

Load WebGi from the iJewel CDN. Then load the Mini Viewer bundle that does not include WebGi:

html
<script src="https://releases.ijewel3d.com/libs/webgi-v0/bundle-0.22.0.js"></script>
<script src="https://releases.ijewel3d.com/libs/mini-viewer/0.6.27/bundle.nowebgi.iife.js"></script>

For a project with a bundler (React, Next.js, Vite), install the packages from their tarball URLs:

json
{
  "dependencies": {
    "@ijewel3d/mini-viewer": "https://releases.ijewel3d.com/libs/web/ijewel3d-mini-viewer-0.6.27.tgz",
    "@types/webgi": "https://releases.ijewel3d.com/libs/webgi-v0/bundle-types-0.22.0.tgz"
  }
}

Keep the WebGi <script> tag from the CDN in your HTML. The tarball @types/webgi gives only the TypeScript types.

Do not install webgi or @types/webgi from the npm registry

The webgi package on npm is an old placeholder (version 0.0.1), not the current engine. @types/webgi is not on npm. npm i @types/webgi returns E404. Use the URLs on this page.

Diamonds show in pink, or the console shows "Shader decode failed" ​

Cause: Your page loads the WebGi bundle from a copy on your server or from node_modules. The diamond shaders decode only when the bundle loads from the iJewel CDN.

Fix: Load WebGi with the CDN <script> tag in How do I install WebGi. Do not bundle a copy of WebGi into your app.

WebGi 0.22.0 and later also sends the ijewel-shader-decode-failed event on window when this occurs:

js
window.addEventListener('ijewel-shader-decode-failed', () => {
  console.warn('Load WebGi from releases.ijewel3d.com')
})

How do I use the SDK in React, Next.js, Vue or Astro? ​

The viewer needs window and WebGL. Create it only in the browser.

FrameworkWhat to do
ReactUse ViewerComponent. Refer to React.
Next.jsTurn off server rendering for the viewer. Refer to "window is not defined" in Next.js.
VueCreate the viewer in onMounted.
AstroPut the viewer in a component with the client:only directive.

In each framework, add the WebGi <script> tag once, before your app bundle. If the viewer starts before WebGi loads, refer to "requires WebGI to be loaded" error.

My site has a Content Security Policy (Shopify Hydrogen, Remix) ​

The SDK sends a domain check to ijewel3d.com. If your CSP blocks it, the console shows "License verification blocked by CSP".

Add these hosts to your policy:

text
script-src  https://releases.ijewel3d.com
connect-src https://ijewel3d.com https://assets.ijewel3d.com

Where do I get the latest version and the changelog? ​

PackageCurrent file
Mini Viewer (script)https://releases.ijewel3d.com/libs/mini-viewer/0.6.27/bundle.nowebgi.iife.js
Mini Viewer (npm tarball)https://releases.ijewel3d.com/libs/web/ijewel3d-mini-viewer-0.6.27.tgz
WebGi (script)https://releases.ijewel3d.com/libs/webgi-v0/bundle-0.22.0.js
WebGi typeshttps://releases.ijewel3d.com/libs/webgi-v0/bundle-types-0.22.0.tgz

The Mini Viewer changes are in the viewer changelog.

Where are the API docs and code examples? ​

Can I use the SDK in a mobile app or another 3D engine? ​

  • Mobile app (native, React Native, Flutter): show the viewer in a WebView or with an iframe embed. Register the hostname of the page if the page uses the SDK.
  • Other engines (Unity, PlayCanvas, react-three-fiber): not supported. WebGi is a web library on three.js, and it has its own renderer and plugins.

How do I make the background transparent, or turn auto-rotate on? ​

In the Mini Viewer, set these viewerOptions:

js
new ijewelViewer.Viewer(container, project, {
  transparentBg: true,          // the page shows behind the model
  runRotateCamera: true,        // turntable rotation after the model loads
  disableInteractionPrompt: true,
  enableZoom: false,
  enablePan: false,             // Mini Viewer 0.6.25 and later
})

All options are in ViewerOptions.

How do I play, pause or stop the GLTF animations in code? ​

Use GLTFAnimationPlugin. Get the viewer from the ijewel-viewer-ready event, or from onViewerReady in React:

js
const anim = viewer.getPluginByType('GLTFAnimation')
anim.loopAnimations = true
anim.animationSpeed = 0.5        // half speed
await anim.playAnimation()       // play all clips
anim.pauseAnimation()
anim.stopAnimation(true)         // stop and go back to the first frame
await anim.playClip('Open')      // play one clip by name

How do I move the camera to a saved camera view? ​

Use CameraViewPlugin. The views that you save in the Playground are in camViews:

js
const views = viewer.getPluginByType('CameraViews')
await views.animateToView(views.camViews[1], 1500) // duration in ms

Can WebGi read glTF KHR_materials_variants? ​

Yes. WebGi reads the extension with GLTFKHRMaterialVariantsPlugin. To show a variant, set its name:

js
viewer.getPluginByType('GLTFKHRMaterialVariantsPlugin').selectedVariant = 'Rose Gold'

How do I take a screenshot of the viewer? ​

Use CanvasSnipperPlugin. getDataUrl() waits for the next frame, then returns the image:

js
const snipper = viewer.getPluginByType('CanvasSnipper')
const image = await snipper.getDataUrl()      // data URL of the canvas
await snipper.downloadSnapshot('ring.png')    // save the file

If getPluginByType() returns undefined, the viewer does not have the plugin. Add it, and pass the plugin class:

js
const snipper = await viewer.getOrAddPlugin(CanvasSnipperPlugin) // the class from the WebGi package of your page

How do I turn off ambient occlusion (SSAO) on one object? ​

Set ssaoDisabled in the userData of the material. Then tell the viewer to render again:

js
mesh.material.userData.ssaoDisabled = true
viewer.setDirty()

Shadows do not update after I show or hide an object ​

After you change the visibility of an object, reset the shadows:

js
mesh.visible = false
viewer.renderer.resetShadows() // the shadows update on the next frame

How do I upgrade WebGi safely? ​

Step 1 of 4
  1. Use a fixed version in the script URL, for example bundle-0.22.0.js.
  2. Before you upgrade, read the WebGi changelog for each version between your version and the new version. Look for the entries with the label Breaking Change or breaking fix.
  3. Use the same version for the @types/webgi tarball.
  4. Test the new version on a staging hostname. Then publish it.

My site does not look like the Playground ​

The environment, the background and the plugin settings are in the scene settings file (.vjson). Load this file into your viewer after the model:

js
await viewer.load('https://example.com/ring.glb')
await viewer.load('https://example.com/ring.vjson')
  • The .vjson file sets only the plugins that your viewer has. The viewer ignores the settings of a plugin that it does not have, and it shows no warning.
  • A different WebGi version can render the same file differently. For example, WebGi 0.20.0 changed how bump maps from older files are read. Refer to How do I upgrade WebGi safely.

How do I use my Playground scene in my own code? ​

You can use the model from the platform, or a model file and a scene settings file.

How do I load, move, scale and remove models in code? ​

viewer.load() adds a file to the scene and returns the new object. autoScale scales the model to fit a sphere with the radius autoScaleRadius (default 2).

js
const model = await viewer.load('https://example.com/ring.glb', {
  autoScale: true,
  autoScaleRadius: 2,
})

model.modelObject.position.set(0, 0.5, 0)   // three.js Object3D
model.modelObject.scale.setScalar(1.2)
viewer.scene.setDirty({ sceneUpdate: true })

To change to a different model, remove the old models first:

js
viewer.scene.disposeSceneModels()   // remove and dispose all models
await viewer.load('https://example.com/ring-2.glb')
  • viewer.scene.removeSceneModels() removes all models, but it does not dispose them.
  • viewer.scene.getModelBounds() returns the bounding box of all models.

How do I apply a material file (.dmat or .pmat) in code? ​

  1. Load the material file with viewer.load().
  2. Apply it with applyMaterial(). The second argument is the name of the material in the model. The name is a regular expression by default.
js
const gem = await viewer.load('https://example.com/white-diamond.dmat')
viewer.assetManager.materials.applyMaterial(gem, 'Gem 01')
  • A .pmat file is a metal or other standard material. A .dmat file is a diamond material.
  • The viewer can load .dmat files only when it has the diamond plugin (DiamondPlugin, type 'Diamond'). The Mini Viewer adds it.
  • If the material in the model is a different type, applyMaterial() makes a new material of the correct type and puts it on the meshes.

How do I extend the renderer (custom shaders, passes, clipping)? ​

WebGi plugins use these base classes. Use the same classes for your own change.

ChangeBase class or objectExample in WebGi
A new pluginAViewerPluginAll plugins
A change to material shadersMaterialExtension with GenericExtensionPluginVignettePlugin, LUTPlugin
A new pass in the render pipelineGenericFilterPluginBloomPlugin
ClippingFragmentClippingExtensionPlugin

A MaterialExtension can add shader code (parsFragmentSnippet, parsVertexSnippet, shaderExtender), uniforms (extraUniforms) and defines (extraDefines). Add it to a material with material.registerMaterialExtensions([extension]).

Threepipe has the same structure. In Threepipe, GenericFilterPlugin has the name PipelinePassPlugin. Refer to the Threepipe docs.

Mini Viewer: use brandingSettings in viewerOptions.

js
new ijewelViewer.Viewer(container, project, {
  brandingSettings: {
    enable: true,                  // show the logo of the project in the viewer
    showLoadingScreenLogo: false,  // no logo on the loading screen
  },
  disableInteractionPrompt: true,  // no hand prompt
  showUiButtons: false,            // no button bar
})

WebGi: change the properties of LoadingScreenPlugin.

js
const loading = viewer.getPluginByType('LoadingScreenPlugin')
loading.logoImage = ''            // no logo. Or the URL of your image.
loading.loadingTextHeader = 'Loading'
loading.showFileNames = false
loading.showProgress = false
loading.background = '#ffffff'
loading.refresh()

The evaluation watermark is a different mark. These settings do not remove it. Refer to How do I register my domain.

How do I animate the camera with scroll (GSAP ScrollTrigger)? ​

The viewer renders a new frame only when something tells it that the scene changed. If you change the camera position directly, call positionUpdated() on each update:

js
const camera = viewer.scene.activeCamera

gsap.to(camera.position, {
  x: 0, y: 2, z: 6,
  scrollTrigger: { trigger: '#section-2', scrub: true },
  onUpdate: () => camera.positionUpdated(),
})

positionUpdated() copies the position to the camera and tells the viewer to render.

WebGi also has ScrollableCameraViewPlugin. This plugin moves the camera between the saved camera views when the page scrolls.

How do I change the settings of a plugin in code? ​

Step 1 of 3
  1. Get the plugin with viewer.getPluginByType('<type>').
  2. Change its properties.
  3. Call viewer.setDirty().
js
const bloom = viewer.getPluginByType('Bloom')
bloom.pass.passObject.intensity = 0.4
bloom.pass.passObject.threshold = 1.5
viewer.setDirty()

Some plugin types: 'Bloom', 'SSReflection', 'SSBevelPlugin', 'GLTFAnimation', 'CameraViews', 'Picking', 'LoadingScreenPlugin', 'Diamond', 'Progressive'.

addBasePlugins(viewer) adds the standard plugins, for example the asset manager, the loading screen, the post-processing plugins, SSR, SSAO and the GLTF animation plugin.

How do I handle clicks and hover on parts of the model? ​

Use PickingPlugin (type 'Picking'). It sends these events: selectedObjectChanged, hoverObjectChanged and hitObject.

js
const picking = viewer.getPluginByType('Picking')

picking.picker.hoverEnabled = true   // hover events are off by default
picking.picker.selectionCondition = (obj) => obj.name.startsWith('Gem')

picking.addEventListener('selectedObjectChanged', (e) => {
  console.log('Selected', e.object?.name)
})
picking.addEventListener('hoverObjectChanged', (e) => {
  console.log('Hover', e.object?.name)
})
  • selectionCondition sets which objects the user can select.
  • In WebGi 0.22.1 and later, set picking.picker.cycleThroughHits = false. Then a second click selects the nearest object again, not the object behind it.

How do I make the viewer faster and use less memory? ​

  • Render fewer pixels. viewer.renderer.renderScale sets the pixel ratio of the canvas. A lower value is faster on phones.
  • Use fewer progressive frames. The viewer renders more frames to make a still image better. maxFrameCount sets the number of frames. Fewer frames are faster, but a still image has less quality.
  • Dispose old models. Before you load a new model, call viewer.scene.disposeSceneModels().
  • Use one viewer. Load a new model into the same viewer. Do not make a new viewer for each model.
js
viewer.renderer.renderScale = Math.min(window.devicePixelRatio, 1.5)
viewer.getPluginByType('Progressive').maxFrameCount = 16

To know when the final image is ready, use isConverged() of the 'Progressive' plugin.

Diamonds and gold look wrong in other glTF viewers ​

WebGi diamonds use the WebGi diamond material (DiamondMaterial). This material is not part of the glTF standard, so other glTF viewers cannot show it. Use the iJewel viewer to show the diamonds correctly.