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": "",
"plugins": {
"WeddingBandBuilder": {
"type": "WeddingBandBuilder",
"version": 1,
"assetBaseUrl": "https://cdn.example.com/wedding-band/assets/",
"profiles": [],
"metals": [],
"finishes": [],
"diamonds": {},
"separationPresets": {},
"defaults": {
"rootScale": 0.1,
"autoFit": false,
"bands": []
}
}
}
}This is a structure to fill in, not yet a loadable project. The required catalogs and opening band are added in the next steps.
Keep UI visibility out of the project JSON. The same project can power either the built-in Wedding Band controls or a Custom UI. Choose the UI mode with Mini Viewer's hideWbbUi option when you create the viewer in step 6.
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.
Metals and finishes
Metals define the color choices. Finishes map every metal ID to its material file:
"metals": [
{
"id": "yellow",
"name": "Yellow Gold",
"iconUrl": "icons/metal-yellow.png"
},
{
"id": "white",
"name": "White Gold",
"iconUrl": "icons/metal-white.png"
}
],
"finishes": [
{
"id": "polished",
"name": "Polished",
"iconUrl": "icons/finish-polished.png",
"files": {
"yellow": "materials/yellow-polished.pmat",
"white": "materials/white-polished.pmat"
}
},
{
"id": "brush",
"name": "Brush",
"iconUrl": "icons/finish-brush.png",
"files": {
"yellow": "materials/yellow-brush.pmat",
"white": "materials/white-brush.pmat"
}
}
]Every key in files must match an ID in metals. When you add a metal, add a material for that metal to every finish you intend to offer.
For consistently named files, filePattern can replace files. Its default is "{metal}-{id}.pmat":
{
"id": "polished",
"name": "Polished",
"filePattern": "materials/{metal}-{id}.pmat"
}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"],
"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 | IDs from metals |
surfaces | IDs from finishes |
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.
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.11/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,
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.
| 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.
- 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.
Continue with Script Tag (Direct Integration) for same-page API usage or Custom UI (Headless) to build your own controls.