Self-Hosted Integration
A self-hosted integration loads a Wedding Band project that you create and serve from your own site or CDN. It does not need a published iJewel Drive file at runtime.
Open the working self-hosted example or inspect its source. Use the example to compare behavior; build your own project JSON with the steps below instead of copying its catalog.
What You Will Host
A typical deployment contains:
wedding-band/
├── index.html
├── app.js
├── wedding-band-project.json
├── scenes/
│ └── wedding-band.vjson
└── assets/
├── profiles/
├── materials/
├── diamonds/
└── icons/The two configuration files have different jobs:
| File | Purpose |
|---|---|
wedding-band-project.json | Catalog, default rings, asset locations, and a reference to the scene file |
wedding-band.vjson | Environment, lighting, ground, post-processing, and optionally the camera exported from Playground |
Self-hosted does not have to mean one server
The page, project JSON, scene VJSON, and assets can use different hosts. Use absolute HTTPS URLs and allow cross-origin requests when files are served from a different origin.
1. Create the Project Shell
Create wedding-band-project.json and begin with the top-level project and an inline WeddingBandBuilder plugin:
{
"name": "My Wedding Band Builder",
"version": "v1",
"basePath": "",
"tryonConfig": {
"enabled": false,
"modelScaleFactor": 1,
"modelPosition": { "x": 0, "y": 0, "z": 0, "isVector3": true },
"modelRotation": { "x": 0, "y": 0, "z": 0, "isVector3": true }
},
"plugins": {
"WeddingBandBuilder": {
"type": "WeddingBandBuilder",
"version": 2,
"assetBaseUrl": "https://cdn.example.com/wedding-band/assets/",
"profiles": [],
"materials": [],
"finishes": [],
"diamonds": {},
"separationPresets": {},
"ui": {
"layout": "boutique"
},
"defaults": {
"rootScale": 0.1,
"autoFit": false,
"bands": []
}
}
}
}This is a structure to fill in, not yet a loadable project. Keep Try-On disabled until you replace the placeholder transform with the fit generated for your band and preview it successfully. The required catalogs and opening band are added in the next steps. Omit tryonConfig entirely if the project will not offer Try-On.
ui.layout chooses the built-in layout. UI visibility remains outside the project JSON: the same project can power either the built-in controls or a Custom UI. Choose visibility with Mini Viewer's hideWbbUi option when you create the viewer in step 6.
Two unrelated version fields
Keep the top-level viewer project field as "version": "v1". The Wedding Band plugin carries its own numeric field, plugins.WeddingBandBuilder.version: 2. The two describe different formats, so never copy one value into the other.
How asset URLs are resolved
assetBaseUrl is the prefix for relative paths inside the Wedding Band plugin:
"assetBaseUrl": "https://cdn.example.com/wedding-band/assets/"profiles/d-shape.3dmbecomeshttps://cdn.example.com/wedding-band/assets/profiles/d-shape.3dm.- An absolute
https://...URL is used unchanged. - Include the trailing
/inassetBaseUrl.
This lets you keep the manifest readable while still overriding an individual asset with an absolute URL when necessary.
2. Form the Asset Catalogs
Build each catalog from the options you actually want to offer. IDs are stable values used by configuration and code; names are customer-facing labels.
Profiles
Add one object to profiles for every band cross-section:
"profiles": [
{
"id": "d-shape",
"name": "D-Shape",
"file": "profiles/d-shape.3dm",
"iconUrl": "icons/profile-d-shape.svg",
"engravingVOffset": {
"oneColor": 0.25,
"multiColor": 0.78
}
},
{
"id": "flat",
"name": "Flat",
"file": "profiles/flat.3dm",
"iconUrl": "icons/profile-flat.svg",
"engravingVOffset": {
"oneColor": 0.72,
"multiColor": 0.69
}
}
]profileIndex in a band's default state is the zero-based position in this array. If you reorder profiles, update every profileIndex that refers to it.
Materials, qualities, and finishes
The material catalog lists the combinations that actually exist. A base material owns its variants/qualities, and each variant maps finish IDs to PMAT URLs:
"finishes": [
{
"id": "polished",
"name": "Polished",
"iconUrl": "icons/finish-polished.png"
},
{
"id": "brush",
"name": "Brush",
"iconUrl": "icons/finish-brush.png"
}
],
"materials": [
{
"id": "yellow",
"name": "Yellow Gold",
"kind": "metal",
"usage": ["band", "inlay", "overlay", "sleeve"],
"iconUrl": "icons/metal-yellow.png",
"variants": [
{
"id": "14k",
"name": "14k",
"files": {
"polished": "materials/yellow/14k/polished.pmat",
"brush": "materials/yellow/14k/brush.pmat"
}
},
{
"id": "18k",
"name": "18k",
"files": {
"polished": "materials/yellow/18k/polished.pmat",
"brush": "materials/yellow/18k/brush.pmat"
}
}
],
"defaultVariant": "18k",
"finishes": ["polished", "brush"],
"defaultFinish": "polished"
},
{
"id": "wood",
"name": "Wood",
"kind": "organic",
"usage": ["inlay", "overlay"],
"variants": [
{
"id": "koa",
"name": "Koa",
"file": "materials/wood/koa/default.pmat"
}
],
"defaultVariant": "koa",
"finishes": [],
"defaultFinish": null
}
]usage controls where the material can appear. Omit it only when the material is valid for every role. files is the selectable finish map. Use singular file for a variant such as wood that has no finish axis.
The older metals plus finishes[].files format is still normalized by the viewer for compatibility. Write new manifests with materials and plugins.WeddingBandBuilder.version: 2.
Diamond models
The builder reuses these two models when it places stones and prongs:
"diamonds": {
"singleModelFile": "diamonds/diamond-single.glb",
"prongsModelFile": "diamonds/diamond-prongs.glb"
}Keep this block in the manifest even when the opening design has no diamonds.
3. Define Color Separation
separationPresets describes how the generated band is divided into material regions. Start with a one-color preset, then add two- and three-color presets if your catalog needs them:
"separationPresets": {
"1 Color": {
"divisions": [1, 1],
"horizontalSep": false,
"useGrooveEdges": false,
"regions": [
{
"slotIndex": 1,
"usePolished": false,
"respectInsidePolished": false
},
{
"slotIndex": 1,
"usePolished": false,
"respectInsidePolished": false
},
{
"slotIndex": 1,
"usePolished": true,
"respectInsidePolished": true
},
{
"slotIndex": 1,
"usePolished": true,
"respectInsidePolished": true
}
]
}
}The object key ("1 Color") is also the value used by defaults.bands[].state.partition and setPartitionByName(). slotIndex is one-based and chooses the customer's material slot for that region.
4. Define the Opening Band
Add at least one band to defaults.bands. Form its state using IDs already declared in the catalogs:
"defaults": {
"rootScale": 0.1,
"autoFit": false,
"bands": [
{
"name": "main",
"displayName": "Wedding Band",
"defaultWidthMm": 4,
"defaultHeightMm": 1.8,
"position": [0, 0, 0],
"rotation": [0, 0, 0],
"state": {
"profileIndex": 0,
"profileWidth": 1,
"profileHeight": 1,
"ringSize": 8.26,
"partition": "1 Color",
"activeMetalTab": 0,
"metals": ["yellow", "yellow", "yellow"],
"variants": ["18k", "18k", "18k"],
"surfaces": ["polished", "polished", "polished"],
"diamond": "none",
"diamondPlacement": 0.5,
"stoneSize": 1.5,
"engravingText": "",
"engravingFont": "Sacramento",
"engravingFontSize": 80,
"edgeType": "None",
"edgeSide": "Both",
"settingType": "none",
"diamondSpan": "Full Ring",
"diamondSpacing": "Stone to Stone",
"diamondCount": 0,
"insidePolished": true,
"splitGroove": false,
"wavyGrooves": false,
"wavySplit": false,
"wavyFrequency": 6,
"wavyAmplitude": 0.3
}
}
]
}The important references are:
| State field | Must refer to |
|---|---|
profileIndex | Position of an entry in profiles |
partition | A key in separationPresets |
metals | Base IDs from materials whose usage includes band |
variants | Variant IDs belonging to the corresponding base material |
surfaces | Finish IDs available in the corresponding variant's files map |
inlays[].metal | Base IDs whose usage includes inlay |
overlays[].metal | Base IDs whose usage includes overlay |
sleeve.metal | A base ID whose usage includes sleeve |
name is the bandName passed to API methods. Add another object to bands for a matching pair, giving it a unique name, state, position, and rotation.
Optional outside and inside features
Add these fields inside a band's state when the opening design needs inlays, rim overlays, or an inside sleeve:
{
"inlays": [
{
"centerZ": 0,
"widthMm": 1.2,
"metal": "wood",
"variant": "koa"
}
],
"overlays": [
{
"side": "left",
"widthMm": 0.8,
"metal": "rose",
"variant": "18k",
"finish": "polished",
"rimCoverage": 0.5
}
],
"sleeve": {
"enabled": true,
"metal": "yellow",
"variant": "18k",
"finish": "polished",
"full": false,
"widthMm": 3
}
}An inlay is a free-positioned stripe on the outer surface. An overlay starts at the named rim and runs inward. rimCoverage ranges from 0 to 1 and controls the overlay on the flat rim face. A partial sleeve is centered across the bore.
The viewer clamps overlapping or overflowing feature dimensions. These features render only with a circular path and a vertical-style material division. They remain material regions, so they do not change ring weight.
autoFit: false prevents the builder from replacing the opening camera. This is important when the camera comes from your Playground scene or from a top-level cameraConfig.
5. Get Scene Settings from Playground
Create the visual environment in iJewel Playground:
- Load a representative wedding band model so lighting decisions are made against the right material and scale.
- Use Scene in the right panel to set the environment, background, ground, shadows, and lights. See Scene Settings.
- Configure the required post-processing and camera view.
- Open Export in the left panel.
- Under Scene Settings, click Download VJSON.
The downloaded .vjson contains the complete scene look. Playground also has an advanced export in right panel → Export → Asset Export → Preset Config Export when you need to choose which plugins are included. See Export Scene Settings for both flows.
Upload the downloaded file to your site or CDN, for example:
https://cdn.example.com/wedding-band/scenes/wedding-band.vjsonAdd the Playground scene to the project JSON
Add sceneConfig as a top-level sibling of plugins. Reference the hosted .vjson; do not paste the VJSON's contents into the Wedding Band plugin:
{
"name": "My Wedding Band Builder",
"version": "v1",
"basePath": "",
"plugins": {
"WeddingBandBuilder": {
"type": "WeddingBandBuilder"
}
},
"sceneConfig": {
"type": "PresetLibraryPlugin",
"VJSON": {
"path": "https://cdn.example.com/wedding-band/scenes/wedding-band.vjson",
"name": "wedding-band.vjson",
"isCustom": true,
"config": {
"usePresetCamera": true,
"usePresetLoadingPlugin": false,
"usePresetInteractionPlugin": false,
"usePresetParallaxMappingPlugin": false,
"usePresetRendererUiPlugin": false,
"usePresetCameraViewPlugin": false,
"usePresetMaterialConfiguratorPlugin": false,
"usePresetSwitchNodePlugin": false
}
}
}
}The short WeddingBandBuilder block above only shows where sceneConfig goes; keep the catalogs and defaults you formed in the earlier steps.
With usePresetCamera: true, the opening camera comes from the Playground VJSON. Keep defaults.autoFit set to false so the Wedding Band Builder does not reframe it.
To manage the opening camera separately, set usePresetCamera to false and add a top-level cameraConfig:
"cameraConfig": {
"position": {
"x": -2.5,
"y": 3.5,
"z": 4.5,
"isVector3": true
},
"target": {
"x": 0,
"y": 0,
"z": 0,
"isVector3": true
}
}Scene file access
The .vjson and every HDR, texture, or other URL referenced inside it must be publicly readable by the browser. Configure CORS on a different-origin CDN and avoid URLs that require dashboard cookies or authentication.
6. Load the Project
Serve the project over HTTP or HTTPS; fetch() will not reliably work from a file:// URL.
Add the verified viewer scripts and a container to your page:
<div id="viewer" style="width: 100%; height: 600px"></div>
<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.18/bundle.nowebgi.iife.js"></script>
<script src="./app.js"></script>Load your JSON in app.js and pass the complete object to Mini Viewer. This example shows the built-in Wedding Band controls:
const useBuiltInWbbUi = true;
async function start() {
const response = await fetch('./wedding-band-project.json');
if (!response.ok) {
throw new Error(`Project request failed: ${response.status}`);
}
const project = await response.json();
new ijewelViewer.Viewer(
document.getElementById('viewer'),
project,
{
showCard: false,
showSwitchNode: false,
showUiButtons: true,
hideTryOn: false,
showConfigurator: false,
showZoomButtons: true,
enableZoom: true,
hideWbbUi: !useBuiltInWbbUi
}
);
}
window.addEventListener('ijewel-viewer-ready', ({ detail }) => {
const plugin = detail.viewer.getPluginByType('WeddingBandBuilder');
const api = plugin.controller;
console.log(api.getAvailableProfiles());
}, { once: true });
start().catch(console.error);hideWbbUi controls the Wedding Band configuration panel. It is independent from showUiButtons, which controls viewer actions such as fullscreen and camera buttons.
hideTryOn: false keeps the Try-On action visible when the top-level tryonConfig.enabled value is true. Mini Viewer isolates the selected band while Try-On is active and restores the complete Wedding Band project when the session ends. A linked pair asks the customer which ring to use; an unlinked pair uses the active ring.
Create the fit in iJewel3D Platform by selecting the Wedding Band project and choosing Edit Default Settings, then copy its top-level tryonConfig into the self-hosted project. If the project does not pass through the Platform, see RingTryonPlugin setup mode. For a custom Try-On button, use Configurator Try-On helpers to list the available bands, load tryonConfig, and prepare the selected band before tryon.start().
| Experience | useBuiltInWbbUi | Result |
|---|---|---|
| Built-in controls | true | Mini Viewer renders the standard Wedding Band panel |
| Custom/headless controls | false | Only the 3D viewer renders; your code builds the controls |
For a headless integration, change useBuiltInWbbUi to false. Keep the ijewel-viewer-ready listener registered before start() so your code can read the controller and build its UI as soon as the viewer is ready.
Validate Before Adding More Options
Build and test in this order:
- One profile, one metal, one finish, one separation preset, and one band.
- The Playground
.vjsonand its referenced resources. - Additional profiles and finish/metal combinations.
- The calibrated Try-On fit with
tryonConfig.enabledset totrue. - A second band, diamonds, icons, pricing, and custom UI controls.
When the first ring does not render, inspect the browser Network panel. A project can be valid JSON while still containing a missing .3dm, .pmat, .glb, .vjson, HDR, or texture URL.
Common checks:
- Validate
wedding-band-project.jsonwith a JSON validator; JSON does not allow comments or trailing commas. - Confirm all URLs return
200over HTTPS. - Confirm every finish contains a file for every offered metal.
- Confirm
profileIndex,partition, metal IDs, and finish IDs point to entries that exist. - Confirm
sceneConfigis outsidepluginsandVJSON.pathpoints to the exported Playground file. - If the Try-On button is missing, confirm that
tryonConfig.enabledistrue,showUiButtonsistrue, andhideTryOnisfalse.
Continue with Script Tag (Direct Integration) for same-page API usage or Custom UI (Headless) to build your own controls.