Skip to content

API Reference ​

Complete reference for the Wedding Band Builder API.

Accessing the API ​

Direct Integration (Same Page) ​

javascript
window.addEventListener('ijewel-viewer-ready', (e) => {
  const viewer = e.detail.viewer;
  const api = viewer.getPluginByType('WeddingBandBuilder').controller;

  api.setWidthMm(4.4);
});

Same-Origin iframe ​

The iframe page fires ijewel-viewer-ready on its own window. That event does not bubble to the parent page. Register a parent callback before the iframe loads, then forward the viewer from a listener inside the iframe page.

Parent page:

html
<iframe id="wbb-iframe" title="Wedding Band Builder"></iframe>
javascript
window.handleWbbViewerReady = (viewer) => {
  const api = viewer.getPluginByType('WeddingBandBuilder').controller;
  api.setWidthMm(4.4);
};

const iframe = document.getElementById('wbb-iframe');
iframe.src = './wbb-viewer.html';

Inside wbb-viewer.html, register this listener before creating Mini Viewer:

javascript
window.addEventListener('ijewel-viewer-ready', ({ detail }) => {
  window.parent.handleWbbViewerReady?.(detail.viewer);
}, { once: true });

This direct callback requires both pages to use the same origin. You can also use the postMessage protocol below for a same-origin iframe.

Cross-Origin (PostMessage) ​

For iframes on a different domain:

javascript
// Parent page sends to iframe
const viewerOrigin = 'https://ijewel3d.com';
iframe.contentWindow.postMessage({
  id: 'req-1',
  method: 'setWidthMm',
  args: [4.4]
}, viewerOrigin);

// Listen for responses from iframe
window.addEventListener('message', (event) => {
  if (event.source !== iframe.contentWindow) return;
  if (event.origin !== viewerOrigin) return;
  if (event.data.id === 'req-1') {
    if (event.data.error) {
      // Structured error: { message, source, details }
      console.log(event.data.error.message);
    } else {
      console.log('Result:', event.data.result);
    }
  }
  if (event.data.event) {
    console.log('Event:', event.data.event, event.data.data);
  }
});

Events (including 'log' and 'error') are automatically forwarded from the iframe to the parent window.

Read Methods ​

getSnapshot(bandName?) ​

Returns a customer-facing summary snapshot for one band. It includes the outside and inside features and each slot's base, variant and finish. Use toJSON() when exact state restoration is required.

javascript
const snapshot = api.getSnapshot('her');

Parameters:

NameTypeDefaultDescription
bandNamestringactive band'her' or 'his'

Returns: RingSnapshot

exportConfig() ​

Exports one RingSnapshot summary for every band included in the project. This is the readable record for a cart, an order line, or a manufacturer handoff.

javascript
const config = api.exportConfig();
// { her: RingSnapshot, his: RingSnapshot }

Returns: Record<string, RingSnapshot>

The summary covers the profile, dimensions, materials — including each slot's base, variant and finish — outside and inside features, diamonds, edges, engraving, and price. importConfig() reads it back and reproduces the same rings.

Pricing is the one thing it does not restore. The snapshot carries computed totals, not the PricingParams that produced them, so remove pricing from each band before you import the record back. Use api.toJSON() when you need the exact internal state, including anything the readable summary does not model.

getBandNames() ​

Returns the IDs of all bands included in the loaded project. Do not assume that both her and his exist—a Drive project can be created with only one.

javascript
const bands = api.getBandNames(); // ['her', 'his'], ['her'], or ['his']

Returns: string[]

getManifest() ​

Returns the loaded Wedding Band manifest, including project catalogs, icons, limits, engraving options, and theme configuration. Prefer the dedicated catalog getters for building UI; use this method when you need project metadata that has no dedicated getter.

javascript
const manifest = api.getManifest();
console.log(manifest?.icons?.logo);

Returns: WeddingBandManifest | null

getSpecSheetData(bandName?) ​

Returns one manufacturing/export payload containing the ring snapshot, price, diamond size information, and actual physical dimensions.

javascript
const {
  snapshot,
  price,
  diamondSizeInfo,
  actualDimsMm,
} = api.getSpecSheetData('her');

Returns:

typescript
{
  snapshot: RingSnapshot;
  price: PriceBreakdown | null;
  diamondSizeInfo: { diameterMm: number; carats: number } | null;
  actualDimsMm: { widthMm: number; heightMm: number };
}

See Specification Sheet for a printable implementation.

getRawState(bandName?) ​

Returns a read-only snapshot of the controller's internal BandState. This is primarily useful for debugging and advanced migration work; storefront order data should use exportConfig() or getSnapshot() for a readable summary, and api.toJSON() when an exact restore is needed.

javascript
const internalState = api.getRawState('her');

Returns: Readonly<BandState>

getActiveBand() ​

Returns the name of the currently active band.

javascript
const band = api.getActiveBand(); // 'her' or 'his'

Returns: string

getDimensions(bandName?) ​

Returns the current dimension state. For compatibility, widthMm and heightMm are legacy field names: their values are profile scale multipliers, not physical millimeters. radiusMm is a physical radius in millimeters.

javascript
const dims = api.getDimensions();
// { widthMm: 1.0, heightMm: 1.0, radiusMm: 8.5 }
console.log(`Width scale: ×${dims.widthMm}`);

Returns: DimensionsSnapshot

getRawProfileDimensions(bandName?) ​

Returns the source profile's width and height at a 1.0 scale, in millimeters.

javascript
const raw = api.getRawProfileDimensions();
// { widthMm: 4.0, heightMm: 1.8 }

Returns: { widthMm: number; heightMm: number }

getActualDimensionsMm(bandName?) ​

Returns the physical profile width and height after applying the current scale multipliers.

javascript
const actual = api.getActualDimensionsMm();
// { widthMm: 4.4, heightMm: 1.8 }

Returns: { widthMm: number; heightMm: number }

getDiamondBaseDiameterMm() ​

Returns the manifest's source diamond diameter at stoneSize = 1.

javascript
const baseDiameterMm = api.getDiamondBaseDiameterMm();

Returns: number

getDiamondSizeInfo(stoneSize) ​

Converts a Wedding Band stone-size multiplier into its physical diameter and approximate round-brilliant carat weight.

javascript
const stone = api.getDiamondSizeInfo(1.5);
// { diameterMm: number, carats: number }

Returns: { diameterMm: number; carats: number }

getMaterials(bandName?) ​

Returns the current partition, material slots, and outside/inside material features. The historical metal field contains the base material ID.

javascript
const mats = api.getMaterials();
// {
//   partition: 2,
//   slots: [
//     { slot: 1, metal: 'White',  base: 'White',  variant: '18k', finish: 'Polished' },
//     { slot: 2, metal: 'Yellow', base: 'Yellow', variant: '14k', finish: 'Hammered' }
//   ],
//   insidePolished: true,
//   splitAtGroove: true,
//   inlays: [{ centerZ: 0, widthMm: 1, metal: 'wood', variant: 'koa' }],
//   overlays: [],
//   sleeve: { enabled: true, metal: 'yellow', variant: '18k', full: true }
// }

Every slot names all three material axes. base is the base material and metal carries the same value under its original name, so code written before variants existed keeps working. variant is the quality or sub-category — 18k versus 14k, or a wood species — and is always filled in: a slot the customer never changed reports the material's own default rather than nothing. finish is empty for a material with no finish axis, such as wood.

Returns: MaterialSnapshot

getDiamonds(bandName?) ​

Returns the current diamond setting configuration, or null if no diamonds are set.

javascript
const diamonds = api.getDiamonds();

Returns: DiamondSnapshot | null

getEdge(bandName?) ​

Returns the current edge configuration.

javascript
const edge = api.getEdge();
// { type: 'Beveled', side: 'Both' }

Returns: EdgeSnapshot

getPrice(bandName?) ​

Returns the full price breakdown, or null if pricing is not configured.

javascript
const price = api.getPrice('her');
console.log(`$${price.totalUsd.toFixed(2)}`);

Returns: PriceBreakdown | null

See Pricing API for detailed pricing documentation.

getWaveParams(bandName?) ​

Returns the clamped wave-path parameters plus the current amplitude ceiling, so a slider can bound itself. frequency stays within 2 to 6, and amplitudeMax is 0.7 / frequency under the engine's slope rule.

javascript
const { frequency, amplitude, amplitudeMax } = api.getWaveParams();

Returns: { frequency: number; amplitude: number; amplitudeMax: number }

getEdges(bandName?) ​

Returns the independent configuration of both band edges.

Returns: { left: { type, width, depth, finish }; right: { type, width, depth, finish } }

getDivisionBoundary(bandName?) ​

Returns the wave that the color seam actually rides on the mesh, or undefined when the seam runs straight. Draw this in any 2D placement surface that must agree with the ring. An enabled Wavy Grooves feature owns the wave and splits the colors along it only when wavy split is on, so a wavy Division can still draw a straight seam.

Returns: { frequency: number; amplitude: number } | undefined

getRelationship(bandName?) ​

Returns the current segment-width ratio, normalized to the partition count.

Returns: number[]

getRingSizeForBand(bandName?) ​

Returns the standard size the band currently reads as, in every system. The spec sheet resolves sizes the same way, so the two can never disagree.

javascript
const { entry, radiusMm, diameterMm, label } = api.getRingSizeForBand();
console.log(label('US'));

Returns: { entry: RingSizeEntry; radiusMm: number; diameterMm: number; label: (system: RingSizeSystem) => string }

getAutoOptimalHeight(bandName?) ​

Returns true when the band tracks the manufacturer's optimal thickness.

Returns: boolean

getThicknessRangeMm(bandName?) ​

Returns the manufacturable thickness range for the active profile at its current width, or null when that profile has no rule table. Use it to bound the height control dynamically.

Returns: { minMm: number; maxMm: number } | null

getMaxDiamondCount(bandName?) ​

Returns how many stones the current setting can physically fit across the band. This is the ceiling the engine clamps requests to, not the count last placed.

Returns: number

isSettingTypeAvailable(settingType, bandName?) ​

Returns true when a stone-setting type suits the band's current profile and path.

Returns: boolean

getUIOptions() ​

Returns the resolved UI option block from the manifest theme.

Returns: Partial<UITheme>

getParallaxEnabled() ​

Returns whether relief parallax mapping is on.

Returns: boolean

Catalog Methods ​

These methods return the available options defined in the project manifest.

getAvailableProfiles() ​

javascript
const profiles = api.getAvailableProfiles();
// [{ index: 0, id: 'd-shape', name: 'D-Shape', thumbnail: '...' }, ...]

Returns: { index: number; id: string; name: string; thumbnail?: string; iconUrl?: string }[]

getAvailableMetals() ​

Returns the base materials allowed for the main band body, as a flat metal-style list. Prefer getAvailableMaterials('band') when the UI also needs variants or usage roles.

javascript
const metals = api.getAvailableMetals();
// [{ id: 'yellow', name: 'Gold', iconUrl: '...' }, ...]

The catalog reports the material icon as iconUrl. An older manifest's thumbnail is carried into the same field, so always read iconUrl.

Returns: { id: string; name: string; iconUrl?: string }[]

getAvailableMaterials(usage?) ​

Returns the base-material catalog. Pass a usage role to build the correct picker for the band body, an inlay, an overlay, or the inside sleeve.

javascript
const bandMaterials = api.getAvailableMaterials('band');
const inlayMaterials = api.getAvailableMaterials('inlay');

usage is optional and accepts 'band', 'inlay', 'overlay', or 'sleeve'. A material with no usage list is returned for every role.

Returns: MaterialEntry[]

getAvailableVariants(baseId) ​

Returns the qualities or subcategories for one base material.

javascript
const variants = api.getAvailableVariants('yellow');
// [{ id: '14k', name: '14k', files: { polished: '...' } }, ...]

Returns: MaterialVariant[]

getAvailableFinishesFor(baseId) ​

Returns only the finishes supported by a base material. It returns an empty array when that material has no finish axis.

javascript
const goldFinishes = api.getAvailableFinishesFor('yellow');
const woodFinishes = api.getAvailableFinishesFor('wood'); // []

Returns: { id: string; name: string; iconUrl?: string }[]

getAvailableFinishes() ​

Returns the complete top-level finish display catalog. Prefer getAvailableFinishesFor(baseId) for a material picker, because one material can support only part of this list.

javascript
const finishes = api.getAvailableFinishes();
// [{ id: 'polished', name: 'Polished', thumbnail: '...' }, ...]

Returns: { id: string; name: string; thumbnail?: string; iconUrl?: string }[]

getAvailablePartitions() ​

javascript
const partitions = api.getAvailablePartitions();
// ['1 Color', '2 Color', '3 Color']

Returns: string[]

getAvailableSettingTypes() ​

javascript
const settings = api.getAvailableSettingTypes();
// [{ id: 'none', name: 'None', iconKey: 'no-diamonds' }, ...]

Returns: { id: string; name: string; iconKey?: string }[]

Use option.id when calling a setter and option.name for the visible label:

javascript
settings.forEach((option) => {
  const button = document.createElement('button');
  button.textContent = option.name;
  button.onclick = () => option.id === 'none'
    ? api.setDiamonds(null)
    : api.setDiamonds({ settingType: option.id });
});

getAvailableEdgeTypes() ​

javascript
const edges = api.getAvailableEdgeTypes();
// [{ id: 'None', name: 'None' }, { id: 'Beveled', name: 'Beveled' }, ...]

Returns: { id: string; name: string }[]

getAvailableEdgeSides() ​

javascript
const sides = api.getAvailableEdgeSides();
// [{ id: 'Left', name: 'Left' }, { id: 'Both', name: 'Both' }, ...]

Returns: { id: string; name: string }[]

getAvailableDiamondSpans() ​

javascript
const spans = api.getAvailableDiamondSpans();
// [{ id: 'half', name: '1/2', fraction: 0.5 }, ...]

Returns: { id: string; name: string; fraction: number | string }[]

getAvailableDiamondSpacings() ​

javascript
const spacings = api.getAvailableDiamondSpacings();
// [{ id: 'half-stone', name: '1/2 Stone', value: 1.5 }, ...]

Returns: { id: string; name: string; value: number | string }[]

getDiamondSpanFraction(span) ​

Returns the numeric or manifest-defined fraction for a diamond span ID.

Returns: number | string

getDiamondSpacingValue(spacing) ​

Returns the numeric or manifest-defined value for a diamond spacing ID.

Returns: number | string

getAvailablePositionSnaps() ​

Returns: { name: string; value: number }[]

getEngravingFonts() ​

Returns the project-defined engraving fonts, or the built-in font catalog when the manifest does not override it.

Returns: string[]

getEngravingSymbols() ​

Returns the symbols offered by the built-in engraving UI.

javascript
const symbols = api.getEngravingSymbols();
// [{ title: 'Heart', char: '♥' }, ...]

Returns: { title: string; char: string }[]

getEngravingDefaults() ​

Returns the resolved project defaults used when a band has no explicit engraving font, size, or rotation.

Returns: { font: string; fontSize: number; rotation: number }

getEngravingVOffset(profileIndex, partition) ​

Returns a profile-specific vertical engraving offset from the manifest, or undefined when the engine should calculate the inner-surface center.

javascript
const offset = api.getEngravingVOffset(0, '1 Color');

Returns: number | undefined

getLimits() ​

Returns the manifest-driven numeric ranges used by the controller. Use these values for sliders instead of hard-coding ranges.

javascript
const limits = api.getLimits();
widthSlider.min = limits.width.min;
widthSlider.max = limits.width.max;
widthSlider.step = limits.width.step || 0.05;

Returns: an object containing { min, max, step? } ranges for width, height, radius, stone size, diamond count and position, engraving, and wavy groove controls.

getAvailableThemes() ​

javascript
const themes = api.getAvailableThemes();
// ['default', 'luxury-gold', 'modern-minimal', 'dark', 'rose-elegant',
//  'coral-modern', 'fresh-teal', 'minimal-blue', 'warm-beige', 'classic-gold', 'ijewel']

Returns: ThemePresetName[]

getAvailablePaths() ​

Returns the extrusion paths, which are the band centerline shapes. The list holds the built-in circular path plus every manifest entry, with icon URLs already resolved.

javascript
const paths = api.getAvailablePaths();
// [{ id: 'circular', name: 'Classic' }, { id: 'wave', name: 'Wave', iconUrl: '...' }]

Returns: { id: string; name: string; iconUrl?: string }[]

getAvailableDivisionTypes(bandName?) ​

Returns the color-division orientations the band can use. An entry with partitionCount suits exactly one partition count.

Returns: { id: string; name: string; iconUrl?: string; partitionCount?: number }[]

getAvailableGrooveTypes() ​

Returns the groove cross sections. type names the engine groove, and angle carries the opening angle of a V groove.

Returns: { id: string; name: string; iconUrl?: string; type?: 'square' | 'v' | 'u' | 'convex-u' | 'milgrain' | 'milgrain2'; angle?: number }[]

getAvailableGrooveFinishes() ​

Returns the interior finishes a groove or joint can take.

Returns: { id: string; name: string; iconUrl?: string }[]

getAvailableGrooveDirections() ​

Returns the groove directions, which run around the ring or across the band.

Returns: { id: string; name: string; iconUrl?: string }[]

getAvailableSideEdgeTypes() ​

Returns the treatments an independent left or right edge can take.

Returns: { id: string; name: string; iconUrl?: string }[]

getAvailableEyeOrientations() ​

Returns the eye-setting orientations. Each entry carries the numeric value the engine applies.

Returns: { id: string; name: string; iconUrl?: string; value: number }[]

getAvailableRingTypes() ​

Returns the ring types a compilation accepts.

Returns: { id: 'wedding' | 'memoire' | 'engagement'; name: string; iconUrl?: string }[]

getAvailableSideSettingOptions(bandName?) ​

Returns the side-setting catalog from the manifest, already filtered by compatibility with the current profile.

Returns: { id: string; name: string; iconKey?: string }[]

getAvailableSettingTypeIds(bandName?) / getAvailableEdgeTypeIds() ​

Return plain id lists, for integrations that do not need the catalog metadata.

Returns: string[]

getRelationshipPresets(partitionOrCount?) ​

Returns the named segment-width ratios offered for a partition, such as 2:1:1.

Returns: { name: string; ratios: number[] }[]

getFreeStonePresetOptions() ​

Returns the curated free-stone patterns, each with the stones it places.

Returns: { id: string; name: string; stones: FreeStone[]; iconUrl?: string }[]

getDiamondColors() / getDefaultDiamondColor() ​

getDiamondColors() returns the stone colors whose material loaded from the manifest. getDefaultDiamondColor() returns the color a stone takes when it names none, which is the first catalog entry. It returns an empty string when no color loaded, and stones then keep the material of the diamond model.

Returns: { id: string; name: string; swatch?: string; iconUrl?: string }[] / string

getRingSizes() / getRingSizeSystems() ​

getRingSizes() returns the standard size catalog that every size control and the spec sheet resolve against. A formula drifts from it by up to half a size, so a UI that offers sizes must offer these. getRingSizeSystems() returns the systems this build can label a ring in.

Returns: readonly RingSizeEntry[] / readonly RingSizeSystem[]

Write Methods ​

All write methods accept an optional bandName parameter ('her' or 'his'). If omitted, the currently active band is used.

setLogLevel(level) ​

Set the minimum log level. Messages below this level are suppressed.

javascript
api.setLogLevel('info');    // show everything
api.setLogLevel('warn');    // errors + warnings (default)
api.setLogLevel('error');   // errors only
api.setLogLevel('silent');  // suppress all output and events
ParameterTypeDefaultDescription
levelLogLevel'warn'Minimum severity to emit

Returns: void

setWidthMm(mm, bandName?) ​

Sets the band width in absolute millimeters, clamped to the product range (1.5–10 mm). The value is stored internally as a profile-relative multiplier (mm ÷ the profile's natural width). Changing the width reconciles the height against the manufacturer rules — the optimal thickness when auto-optimal height is enabled, otherwise a clamp to the manufacturable range. Out-of-range values are clamped and emit validation:warning.

javascript
api.setWidthMm(4.4); // band is now 4.4 mm wide

Returns: void

setHeightMm(mm, bandName?) ​

Sets the band thickness (height) in absolute millimeters, clamped to the manufacturable range — the manufacturer possibility rule for the active profile + width when one exists, else the profile's natural height × 0.5–1.5.

javascript
api.setHeightMm(1.8);

Returns: void

setWidthMultiplier(multiplier, bandName?) / setHeightMultiplier(multiplier, bandName?) ​

Set width or height as a profile-relative multiplier: 1.0 = the active profile's natural dimension. Width is bounded by the same 1.5–10 mm product range as setWidthMm; height by 0.5–1.5. Changing the width reconciles the height as above.

javascript
api.setWidthMultiplier(1.1);  // 110% of the source profile width
api.setHeightMultiplier(0.95);

Returns: void

setWidth(multiplier, bandName?) / setHeight(multiplier, bandName?) deprecated ​

Deprecated aliases of setWidthMultiplier / setHeightMultiplier. Despite the historical names, the argument is a multiplier, not millimeters. Calling them logs a deprecation warning; use setWidthMultiplier()/setHeightMultiplier() or setWidthMm()/setHeightMm() instead.

Note there is no setRingSizeMm(): setRingSize() already takes the inner radius in millimeters.

Returns: void

setRingSize(radiusMm, bandName?) ​

Sets the ring's inner radius in millimeters. If your UI works with inner diameter, divide the diameter by two before calling this method.

javascript
api.setRingSize(8.5);      // 17 mm inner diameter
api.setRingSize(10, 'his');

The value is clamped to getLimits().radius.

Returns: void

setProfile(indexOrName, bandName?) ​

Sets the ring profile by index or name. Returns a promise because the profile geometry needs to be loaded.

javascript
await api.setProfile(0);       // by index
await api.setProfile('Flat');  // by name

Returns: Promise<void>

WARNING

setProfile() is async. Always await it before reading dimensions or other state that depends on the new geometry:

javascript
await api.setProfile(0);
const dims = api.getDimensions(); // safe, new geometry is loaded

Without the await, you may read stale dimensions from the previous profile.

setMaterial(slot, metal, finish, bandName?) ​

Sets the base material and finish for a specific slot. It does not select a variant. Use setMaterialRef() for new interfaces.

javascript
await api.setMaterial(1, 'Yellow', 'Polished');    // Slot 1
await api.setMaterial(2, 'White', 'Hammered');     // Slot 2
await api.setMaterial(3, 'Rose', 'Brush');         // Slot 3
ParameterTypeDescription
slotnumberOne-based material slot number
metalstringBase material ID or accepted display name
finishstringFinish ID or accepted display name

Returns: Promise<void>

setMaterialRef(slot, ref, bandName?) ​

Sets a full base, variant, and finish reference for one material slot.

javascript
await api.setMaterialRef(1, {
  base: 'yellow',
  variant: '18k',
  finish: 'polished',
});

Omitted variant and finish values keep the current slot values. When the base changes, the controller clears a stale finish for a finishless material. It otherwise replaces an unsupported finish with polished or the first available finish. A variant the new base material does not offer is dropped — the slot falls back to that material's default and a validation:warning names the variants it could have had.

Returns: Promise<void>

setPartition(numColors, bandName?) ​

Sets the number of material color zones.

javascript
await api.setPartition(2); // 2-color band
ParameterTypeDescription
numColorsnumberNumber of material zones supported by a manifest preset

Returns: Promise<void>

setPartitionByName(presetName, bandName?) ​

Applies an exact partition preset from getAvailablePartitions(). Use this instead of setPartition() for named layouts such as 2 Color Vertical.

javascript
const preset = api.getAvailablePartitions()[0];
await api.setPartitionByName(preset);

Returns: Promise<void>

setDiamonds(config, bandName?) ​

Configures diamond settings. Accepts a partial DiamondSnapshot — only the fields you pass are changed. Pass null to remove diamonds.

javascript
api.setDiamonds({
  settingType: 'Channel',
  span: 'half',
  spacing: 'half-stone',
  count: 12,
  stoneSize: 1.5,   // 0.5–3.0
});

// A row of stones across the band at 45°
api.setDiamonds({ settingType: 'Cross Bezel', count: 4, positionAngle: 45 });

// Tension setting with two stones and a custom bridge width
api.setDiamonds({ settingType: 'Tension', count: 2, bridgeWidth: 2.4 });

// Remove diamonds
api.setDiamonds(null);

Available settingType values: Channel, Channel All Around, Bezel, Prong, Cross Bezel, Cross Channel, Cross Around, Tension, Tension Diagonal, Eye, Free (use getAvailableSettingTypes() — some depend on the profile). Type-specific options: positionAngle (cross, tension, eye — degrees around the ring), bridgeWidth (tension), eyeOrient (eye — 0, 90, ±45), freeStones (free placement). Side-face settings have their own API — see Side stone settings.

Returns: void

Diamond size ​

Stone size is the stoneSize field. Update it on its own by passing only that field:

javascript
api.setDiamonds({ stoneSize: 2.0 }); // 0.5–3.0

Diamond position ​

Position along the ring's width is the position field. It is automatically clamped based on band width, diamond size, and groove width to keep diamonds inside the ring:

javascript
api.setDiamonds({ position: -1 });    // Left
api.setDiamonds({ position: 0 });     // Center (default)
api.setDiamonds({ position: 1 });     // Right
api.setDiamonds({ position: 0.5 });   // Between center and right
FieldTypeDescription
positionnumberPosition from -1 (left) to 1 (right), 0 = center

INFO

Diamond position is automatically reset to center (0) when wavy grooves are enabled.

setEdge(type, side?, bandName?) ​

Sets the edge treatment.

javascript
api.setEdge('Beveled', 'Both');
api.setEdge('None'); // Remove edge
ParameterTypeDefaultDescription
typestringEdge type (see getAvailableEdgeTypes())
sidestringcurrentSide ID from getAvailableEdgeSides() ('Left', 'Right', or 'Both')

Returns: void

setEngraving(text, font?, fontSize?, bandName?, rotation?) ​

Sets the interior engraving text. Pass null to remove.

javascript
api.setEngraving('Forever & Always', 'serif', 80);

// Remove engraving
api.setEngraving(null);
ParameterTypeDefaultDescription
textstring | nullEngraving text, or null to remove
fontstringcurrentFont family name
fontSizenumbercurrentFont size in pixels
bandNamestringactive bandRegistered band name
rotationnumbercurrentEngraving rotation, clamped to the manifest limit

Returns: void

setInsidePolished(value, bandName?) ​

Toggles whether the ring interior is polished.

javascript
await api.setInsidePolished(true);

Returns: Promise<void>

setPath(pathId, bandName?) ​

Sets the extrusion path, which is the band centerline shape. circular is the built-in default, and other ids come from the manifest path registry.

A custom path drops the settings that need a circular band — the Tension, Eye and Side family — and drops engraving. Regular stone settings ride the path.

javascript
await api.setPath('wave');

Returns: Promise<void>

setPathStrength(strength, bandName?) ​

Sets the path displacement strength. 1 gives the full wave, and 0 matches the classic band.

Returns: void

setWaveParams(params, bandName?) ​

Sets the frequency and amplitude of the built-in wave path. Frequency is an integer from 2 to 6. Amplitude is a fraction of the radius, and the engine clamps it by the slope rule amplitude x frequency <= 0.7. Both values apply only while the wave path is active.

javascript
await api.setWaveParams({ frequency: 4, amplitude: 0.12 });

Returns: Promise<void>

setRelationship(ratio, bandName?) ​

Sets the width ratio between color segments. The ratio length must equal the current partition count. Pass null to return to a uniform split.

javascript
await api.setRelationship([2, 1, 1]);

Returns: Promise<void>

setRingSizeByStandard(system, size, bandName?) ​

Sets the ring size by its standard designation. The value resolves through the size table, so the band lands on an exact standard radius rather than on a formula's approximation. An unknown designation is refused with a validation:warning event.

javascript
api.setRingSizeByStandard('US', 7);
api.setRingSizeByStandard('UK', 'N½');

Returns: void

setAutoOptimalHeight(on, bandName?) ​

Turns on automatic thickness. The band then takes the manufacturer's optimal height for its width whenever the width changes. Turning it on applies the optimal height immediately.

Returns: void

setEdgeSide(side, cfg, bandName?) ​

Configures one edge independently. Pass 'left' or 'right' and any of type, width, depth and finish. An edge that turns on takes a polished finish unless the call names another.

javascript
api.setEdgeSide('left', { type: 'Edge', width: 0.3, depth: 0.2, finish: 'matt' });

Returns: void

setMilgrain(cfg, bandName?) ​

Sets the band-wide milgrain bead style: the bead type, its size, and its spacing. Milgrain turns on per host, through an edge of type Milgrain or a groove or joint of type milgrain, never through this call. Legacy enabled, onEdges and onGrooves inputs are accepted and ignored.

javascript
api.setMilgrain({ beadType: 'round', sizeMm: 0.18, spacingFactor: 1.1 });

Returns: void

setEngravingStyle(color?, roughness?, bandName?, bumpOnly?, bumpScale?, mode?, bump?) ​

Sets the appearance of the engraved marks. color is a diffuse tint multiplied into the metal color, and '#ffffff' or null means no tint. roughness runs from 0, which stays polished, to 1, which is fully matte. bumpOnly gives pure relief with no roughness or tint. bumpScale is in engine units, and a negative value embosses.

javascript
await api.setEngravingStyle('#ffffff', 0.6, undefined, false, 0.4, 'engrave');

Returns: Promise<void>

setInnerStone(cfg, bandName?) ​

Sets the hidden stone, which is a single flush or bezel-set stone in the bore next to the engraving. Its position is an explicit angle in degrees around the ring, independent of where the engraving text sits. Pass enabled: false to remove it.

javascript
await api.setInnerStone({ enabled: true, angleDeg: 180, stoneSize: 1.2 });

Returns: Promise<void>

setDiamondColor(colorId, bandName?) ​

Sets the stone color for one band. His and her rings keep independent colors even though they share the diamond model. Pass null to restore the material of the model.

Returns: void

setParallaxEnabled(enabled) ​

Turns relief parallax mapping on or off. The extension applies to every material with a bump map, which covers engraving and bump finishes such as brush, so the setting is viewer-wide rather than per band.

Returns: void

Outside and Inside Material Features ​

Inlays and overlays paint regions on the outer surface. A sleeve paints the inside bore. These features change material assignment only; they do not add solid geometry or change weight.

They render only on a circular path with a plain vertical-style material division. Axial, diagonal, and wavy divisions do not support them. Use areOutsideFeaturesAvailable() before showing custom controls.

setOutsideFeatures(features, bandName?) ​

Replaces all inlays and overlays in one rebuild. Use this method when several features change together to avoid rendering intermediate states.

javascript
await api.setOutsideFeatures({
  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: true,
  },
});

The inlays and overlays arrays replace their current arrays. Omitted arrays therefore clear those feature types. Omit sleeve to preserve the current sleeve, or pass sleeve: null to remove it.

The controller accepts up to three inlays and one overlay on each rim. It clamps feature widths and positions to prevent overlap and edge overflow.

Returns: Promise<void>

setInlay(index, inlay, bandName?) ​

Adds an inlay at the next index or updates an existing inlay. centerZ is the stripe center in millimeters from the band's center width. widthMm is the stripe width.

javascript
await api.setInlay(0, {
  centerZ: 0,
  widthMm: 1,
  metal: 'marble',
  variant: '1',
});

The controller keeps inlays sorted by centerZ. An index is not a stable identifier after a move. Read getInlays() again after each update.

Returns: Promise<void>

removeInlay(index, bandName?) / getInlays(bandName?) ​

Removes one inlay or returns copies of all current inlay configurations.

javascript
await api.removeInlay(0);
const inlays = api.getInlays();

Returns: Promise<void> / InlayConfig[]

getInlayBounds(index, bandName?) ​

Returns the legal center range and maximum width for an inlay. Re-read these bounds after any band-width, inlay, or overlay change.

javascript
const { centerMin, centerMax, widthMax } = api.getInlayBounds(0);

Returns: { centerMin: number; centerMax: number; widthMax: number }

setOverlay(side, overlay, bandName?) ​

Adds or updates the overlay anchored to the left or right rim. Only one overlay can exist on each side.

javascript
await api.setOverlay('right', {
  widthMm: 1,
  metal: 'white',
  variant: '18k',
  finish: 'brush',
  rimCoverage: 1,
});

rimCoverage controls how far the material reaches down the flat rim face. Use 0 for none, 1 for the complete face, or an intermediate fraction. rimCoverage takes precedence over the older coversRim boolean.

Returns: Promise<void>

removeOverlay(side, bandName?) / getOverlays(bandName?) ​

Removes one rim overlay or returns copies of all overlay configurations.

Returns: Promise<void> / OverlayConfig[]

getOverlayWidthMax(side, bandName?) ​

Returns the largest permitted overlay width for one side after accounting for the band width, other overlays, and inlays.

Returns: number

setSleeve(sleeve, bandName?) / getSleeve(bandName?) ​

Sets the material on the inside bore. Pass a boolean to change only the enabled state. A full sleeve covers the complete bore. A partial sleeve is centered and uses widthMm.

javascript
await api.setSleeve({
  enabled: true,
  metal: 'yellow',
  variant: '18k',
  finish: 'polished',
  full: false,
  widthMm: 3,
});

const sleeve = api.getSleeve();

Changing the sleeve reapplies interior engraving against the new bore material.

Returns: Promise<void> / SleeveConfig | undefined

areOutsideFeaturesAvailable(bandName?) ​

Returns true when the current path and division can render inlays, overlays, and a sleeve.

Returns: boolean

outsideFeaturesFit(bandName?) ​

Returns false when the current inlay and overlay set overlaps or exceeds the band width. Public setters clamp invalid dimensions, but this method is useful when validating imported state.

Returns: boolean

setSplitAtGroove(value, bandName?) ​

Toggles whether material splits align to grooves.

javascript
await api.setSplitAtGroove(true);

Returns: Promise<void>

setWavyGrooves(enabled, frequency?, amplitude?, wavySplit?, bandName?) ​

Enables or disables wavy groove patterns.

javascript
api.setWavyGrooves(true, 8, 0.3);  // Enable with frequency 8, amplitude 0.3
api.setWavyGrooves(true, 8, 0.3, true); // Also split/separate the wave
api.setWavyGrooves(false);          // Disable
ParameterTypeDefaultDescription
enabledbooleanEnable or disable wavy grooves
frequencynumbercurrentWave frequency
amplitudenumbercurrentWave amplitude
wavySplitbooleancurrentEnable separation along the wavy groove
bandNamestringactive bandRegistered band name

Returns: void

INFO

Enabling wavy grooves automatically resets the diamond position to center (0), since offset diamond positions are not supported with wavy grooves.

Division methods ​

  • setDivision(division, bandName?)
  • setDivisionParams(frequency?, amplitude?, bandName?)
  • setDivisionParams(params, bandName?)
  • getDivision(bandName?)

Sets how the color zones separate: 'vertical', 'axial', 'diagonal', or 'wavy'. setDivisionParams adjusts the wavy/diagonal boundary. frequency is the number of wave periods around the band and applies to 'wavy' only; amplitude (0–1) is the boundary excursion and applies to both 'wavy' and 'diagonal'.

javascript
await api.setDivision('wavy');
await api.setDivisionParams(4, 0.4); // frequency, amplitude

// Object form, supported by Mini Viewer 0.6.18 and later.
await api.setDivisionParams({ frequency: 4, amplitude: 0.4 });

TIP

Mini Viewer 0.6.18 and later accept both call shapes. On an older viewer, use the positional form.

setSegmentWidthsMm(widths, bandName?) / getSegmentWidthsMm(bandName?) ​

Per-zone widths in millimeters for multi-color partitions (e.g. a 2:1:1 split).

setSeparationGroove(cfg, bandName?) / getSeparationGroove(bandName?) ​

Configures the joint groove on color boundaries: { enabled?, type?, width?, depth?, angle?, finish?, boundaries? }. boundaries is a per-boundary enable array (disc 1–2, 2–3, ...). Works on straight, diagonal, and wavy divisions alike.

javascript
api.setSeparationGroove({ type: 'v', angle: 60, depth: 0.15 });
api.setSeparationGroove({ enabled: false });   // clean grooveless seam

Design grooves ​

Freely placed decorative grooves: getDesignGrooves(), addDesignGroove(groove?), updateDesignGroove(index, changes), removeDesignGroove(index?), clearDesignGrooves(). Each groove: { type, width, depth, position, angle?, finish?, orientation?, positionAngle? } — orientation: 'horizontal' runs the groove across the band at positionAngle degrees.

Side stone settings ​

Stones on the band's flat side faces, independent of the top setting:

  • setSideSetting(type, bandName?) / getSideSetting(bandName?) — 'none', 'Side Bezel', 'Side Channel', 'Side Prong'
  • getAvailableSideSettingTypes(bandName?) — filtered by profile compatibility (side settings need a flat side face)
  • setSideBezelSides(sides, sourceSide?, bandName?) — 'left', 'right', or 'both'; newly enabled sides clone from sourceSide
  • setSideBezelSide(side, config, bandName?) — per-side span / count / spacing / stone size
  • copySideBezelSide(from, bandName?) — copy one side's settings to the other
  • getSideBezel(bandName?) — full per-side state including computed stone counts

Free stone placement ​

The 'Free' setting type places individual bezel stones anywhere on the band:

  • getFreeStones() / setFreeStones(stones) — each stone: { angleDeg, offset, size } (offset −1..1 across the band)
  • addFreeStone(size?), updateFreeStone(index, changes), removeFreeStone(index), clearFreeStones()
  • applyFreeStonePreset(name) / getFreeStonePresets() — Scatter, Orbit, Cascade, Constellation, Wave

setSmoothSeats(on, bandName?) / getSmoothSeats(bandName?) ​

Switches bezel-style settings (Bezel, Cross Bezel, Free, Side Bezel) between classic sharp-rim pockets and smooth scooped seats that blend tangentially into the surface.

History ​

The builder records every configuration change, so a UI can offer undo and redo without tracking state itself.

undo() / redo() ​

Step back or forward by exactly one change. Calls queue, so rapid presses walk the history instead of collapsing into one step. redo() returns false when nothing was undone.

javascript
if (api.canUndo()) await api.undo();

Returns: Promise<boolean>

canUndo() / canRedo() ​

Report whether a step exists in each direction. The history:changed event fires whenever either answer changes.

Returns: boolean

Saved Configurations ​

A saved configuration is a complete ring design under a short readable id, such as WB-BA5A-DZ58, with a viewer snapshot as its thumbnail.

Storage is localStorage until you supply somewhere else to put it. That is the right default for a shopper, whose designs never need to leave their browser. To make one project's designs appear in every embed of it, on any device, give the viewer a project catalogue host — see The project design catalogue below.

To hand a shopper a link that carries their design with no storage at all, see Design Links.

saveConfiguration(name?) ​

Saves the current configuration and returns its id and name, or null when the save fails.

javascript
const saved = await api.saveConfiguration('Emma and Tom');

Returns: Promise<{ id: string; name: string } | null>

loadConfiguration(id) ​

Loads a saved configuration. Returns false when the id is unknown.

Returns: Promise<boolean>

listConfigurations() ​

Returns the saved configurations, newest first, with id, name, date and thumbnail.

Returns: Promise<SavedConfigSummary[]>

deleteConfiguration(id) ​

Deletes one saved configuration.

Returns: Promise<void>

setConfigStorageAdapter(adapter) ​

Replaces the persistence backend, so saved designs can live in your own REST service or in host-supplied storage instead of localStorage.

javascript
api.setConfigStorageAdapter({
  async list() { /* … */ },
  async save(record) { /* … */ },
  async load(id) { /* … */ },
  async remove(id) { /* … */ },
});

Returns: void

The project design catalogue ​

Saved designs can live in the project instead of the browser. A design saved once then appears in every embed of that project, on any device.

This is a viewer option, not an API call. Set wbbDesignHost when you create the viewer. Mini Viewer installs it on the builder for you.

javascript
new ijewelViewer.Viewer(container, project, {
  wbbDesignHost: {
    // Write the whole catalogue and return its public URL.
    async save(designs) {
      return myBackend.putProjectFile('saved-designs.json', designs);
    },
    // Optional, but recommended. See the note below.
    async list() {
      return myBackend.getProjectFile('saved-designs.json');
    },
  },
});

Omit the option to keep saving to localStorage.

save receives the complete catalogue and rewrites it whole, so the URL it returns must serve exactly what it was given. The file has to be publicly readable: a shopper reads it without signing in.

Implement list if two people can save to the same project. Every save writes a new file at a new URL, so a viewer that loaded before someone else's save holds a stale catalogue. list re-reads the current one first, and without it that save would drop the other person's designs.

When the host is unavailable, saving falls back to localStorage rather than failing. A catalogue that cannot be read is never replaced with an empty one.

Same-page integrations only

wbbDesignHost holds functions, so it cannot cross the postMessage bridge. A cross-origin iframe host must set it inside the iframe page. The same limit applies to setConfigStorageAdapter().

See ViewerOptions for the full option list.

A design link carries the whole design in the URL. Nothing is written to a server, so there is no record to keep and nothing to clean up. Use it for a share button, or to put a design in a product field on your own storefront.

Only the restorable part travels — the bands, their pricing, and the ring compilation. The manifest and the thumbnail do not, so a typical design compresses to roughly 500 characters.

getDesignCode() ​

The current design as a compact code, with no link around it. Use it when you want to keep the design in your own field, such as a line on an order.

javascript
const code = await api.getDesignCode();
// Store it on the order, then restore it later with applyDesignLink(code).

Returns null when there is no band state to share.

Returns: Promise<string | null>

getShareUrl() ​

A ready link to the current design, with the code in the URL fragment.

javascript
const url = await api.getShareUrl();
await navigator.clipboard.writeText(url);

The code goes in the fragment on purpose. A fragment never reaches a server log, which matters because a design can carry engraving text, and it avoids proxy limits on query-string length. Any route or query already in the address is kept, so a storefront path such as https://shop.example/#/product/42 survives being shared.

Returns null when there is no design to share, or no address to build on.

Returns: Promise<string | null>

setShareBaseUrl(url) ​

Sets the address getShareUrl() builds on.

javascript
api.setShareBaseUrl('https://shop.example/#/product/42');

Needed inside a cross-origin iframe, where the builder cannot read the storefront's address and would otherwise build a link to the iframe itself.

Returns: void

Restores a design. Accepts a bare code, a ?design= URL, or a #?design= URL, so you can pass whatever the shopper pasted.

javascript
const restored = await api.applyDesignLink(window.location.href);
if (!restored) {
  // The text carried no readable design, or none of its rings exist here.
}

Returns false, and emits a validation:warning, when the text holds no readable design or when none of its bands exist in this project. A design made against a different manifest is still applied, with a warning, since the same catalogue served from another URL is normal between staging and production.

This is also the only way a cross-origin host can restore an exact design. The postMessage bridge can call toJSON() but not the plugin's fromJSON().

Returns: Promise<boolean>

Manufacturing Export ​

exportManufacturing(config?) ​

Returns manufacturing data in the metrix and confmetrix format: per-zone volumes and weights, grooves, the stone layout, and hallmark engraving. Pass a partial config to override the client vocabulary, such as alloy names or profile ids.

Use this for a production handoff. Use getSpecSheetData() for the readable summary, and api.toJSON() for restorable state.

javascript
const rows = api.exportManufacturing();

Returns: any[]

Band Linking ​

setBandsLinked(linked) / isBandsLinked() ​

Link or unlink the wedding bands, overriding ui.linkBands in the manifest. Linking records the state of every band and changes none of them, so only later edits mirror across. Ring size and engraving stay personal.

Returns: void / boolean

Bead Library ​

The milgrain bead designer stores custom beads in localStorage alongside the built-in presets.

getBeadTypes() ​

Returns the built-in bead presets plus the saved beads of this user. A saved entry carries custom: true.

Returns: { id: string; name: string; params: MilgrainBeadParams; custom?: boolean }[]

saveBeadType(name, params) / deleteBeadType(id) ​

Save a bead into the library and return its id, or delete one by id.

Returns: string / void

createBeadPreview(canvas) ​

Attaches a live 3D bead preview to a canvas. The preview uses polished white metal under the same HDR environment as the builder viewer.

Call dispose() when the modal closes. The renderer holds GPU resources until you do.

javascript
const preview = api.createBeadPreview(canvasEl);
preview.update(params);
preview.render();
// later
preview.dispose();

Returns: BeadPreviewRenderer — { update(params), render(), dispose() }

Plugin Serialization (Advanced) ​

The controller's toJSON() delegates to the WeddingBandBuilderPlugin and returns complete project/plugin state. Full restoration uses the plugin's fromJSON() method. This format preserves raw band states, including material variants and outside/inside features.

toJSON() ​

Exports the complete plugin state as a JSON-serializable object. Includes the manifest URL, all band states, and pricing configuration.

javascript
const plugin = viewer.getPluginByType('WeddingBandBuilder');
const data = api.toJSON(); // equivalent to plugin.toJSON()
// Save or transmit the configuration
localStorage.setItem('wbb-state', JSON.stringify(data));

Returns: { type, manifestUrl? or inline manifest fields, bands?, pricing?, rings? }

fromJSON(data) ​

Restores plugin state from a previously exported JSON object. If bands are already loaded, it applies states in-place without recreating geometry. If the plugin hasn't been initialized yet, it performs a full cold load.

javascript
const plugin = viewer.getPluginByType('WeddingBandBuilder');
const saved = JSON.parse(localStorage.getItem('wbb-state'));
await plugin.fromJSON(saved);

Returns: the plugin instance (or a promise resolving after cold loading)

Cross-origin restoration

The postMessage bridge can call controller toJSON(), but it cannot call the plugin-only fromJSON() method. A cross-origin iframe must load full saved state inside its viewer page or expose an application-specific restore command.

Batch Operations ​

batch(config, bandName?) ​

Apply multiple changes in a single operation. This is more efficient than calling individual setters because it triggers only one geometry rebuild.

javascript
await api.batch({
  profile: { name: 'D-Shape' },
  dimensions: { widthMm: 1.1, heightMm: 0.95 },
  materials: {
    partition: 2,
    slots: [
      { slot: 1, metal: 'White', finish: 'Polished' },
      { slot: 2, metal: 'Yellow', finish: 'Hammered' },
    ],
    insidePolished: true,
    splitAtGroove: true,
  },
  diamonds: {
    settingType: 'Channel',
    span: 'half',
    spacing: 'half-stone',
    count: 12,
    stoneSize: 1.5,
  },
  edge: { type: 'Beveled', side: 'Both' },
  engraving: { text: 'Forever', font: 'serif' },
  pricing: {
    metalPricePerGram: 52.0,
    markupMultiplier: 2.5,
  },
});

All fields in BatchConfig are optional. Include only the sections you want to change.

batch().materials applies the partition, the slots — base, variant and finish alike — inside polish, split state, inlays, overlays, and the sleeve. It touches only the keys the config names: an absent inlays or overlays list leaves the current one in place, and sleeve: null removes the sleeve while an absent sleeve key leaves it alone.

Returns: Promise<void>

importConfig(config) ​

Applies one BatchConfig to each named band. A RingSnapshot is a valid BatchConfig after you remove its computed pricing value, as shown below. Bands the config does not name are left alone, and so is a name that is not registered in the project.

javascript
await api.importConfig({
  her: {
    profile: { name: 'Comfort' },
    dimensions: { widthMm: 0.9 },
    materials: {
      partition: 1,
      slots: [{ slot: 1, metal: 'Rose', finish: 'Polished' }],
    },
  },
  his: {
    profile: { name: 'Flat' },
    dimensions: { widthMm: 1.2 },
    materials: {
      partition: 1,
      slots: [{ slot: 1, metal: 'White', finish: 'Brush' }],
    },
  },
});
javascript
// Round-trip an order record. Drop the computed price first — see below.
const order = api.exportConfig();
for (const band of Object.values(order)) delete band.pricing;

await api.importConfig(order);

importConfig() restores the profile, dimensions, material slots with their variants, inlays, overlays, the sleeve, diamonds, edges, and engraving.

Remove pricing before you re-import

exportConfig() writes a computed price breakdown into pricing. On the way in, that same key means the PricingParams that produce a price. importConfig() merges what it finds there into the stored pricing parameters, so a raw round trip pollutes them. Delete pricing from each band first, as above. Then set the real values with setPricingParams().

Use plugin.fromJSON(savedPluginState) when you need the exact internal state.

Returns: Promise<void>

applyBandState(bandName, state) ​

Applies a complete BandState to one band and resolves after the profile and material rebuilds finish. Use it to restore a state object you stored yourself. Mirroring is suspended for the duration, so a linked pair does not flatten one band's state onto the other.

javascript
await api.applyBandState('her', savedState);

Returns: Promise<void>

switchBand(bandName) ​

Switches the active band between "her" and "his".

javascript
api.switchBand('his');

Returns: void

Fires: band:switched

WARNING

Switching bands does not update your custom UI automatically. In headless mode, listen to band:switched and refresh all controls from the new band's snapshot:

javascript
api.events.on('band:switched', (data) => {
  const snapshot = api.getSnapshot(data.to);
  updateAllControls(snapshot);
});

Ring Compilation ​

Manage the set of rings in the scene: parametric wedding bands plus pre-modelled engagement and memoire rings from the catalog. All changes fire rings:changed.

getRings() ​

Returns the compilation in scene order.

javascript
api.getRings();
// [{ id: 'her', type: 'wedding', name: 'Her Ring' },
//  { id: 'ring-3', type: 'engagement', name: 'Engagement 1',
//    catalogId: 'ring-1', metalId: 'white', visible: true }]

Returns: { id, type, name, catalogId?, metalId?, visible? }[]

getRingCatalog(type) ​

Catalog entries available for a type, with resolved thumbnail URLs.

javascript
const models = api.getRingCatalog('engagement');
// [{ id: 'ring-1', name: 'Solitaire I', thumbnail: 'https://...' }, ...]

Parameters: type — 'engagement' or 'memoire'

addRing(type, catalogId?) ​

Adds a ring and returns its id. Wedding rings are new parametric bands; engagement/memoire load the given catalog model (first entry when omitted) with white gold applied. Async — resolves when the model is in the scene.

javascript
const id = await api.addRing('engagement', 'ring-2');

removeRing(id) ​

Removes a ring. The last remaining wedding band cannot be removed.

moveRing(id, direction) ​

Moves a ring one slot 'left' or 'right'; the 3D layout follows.

renameRing(id, name) ​

Sets the display name shown on the ring's tab and in the manager dialog.

swapRingModel(id, catalogId) ​

Swaps a loaded ring's model in place — same id, tab, name, slot, and metal. The current model stays visible until the replacement has loaded. Async.

setRingMetal(id, metalId) / getRingMetal(id) ​

Applies a metal variant ('white', 'yellow', 'rose') to a loaded ring. Uses the same polished .pmat materials as the wedding bands, so colors match across the compilation.

setRingVisible(id, visible) ​

Shows or hides a ring without removing it (it keeps its slot). The camera re-frames the visible scene and shadows rebake.

focusRing(id) ​

Smoothly centers the camera on one ring, any type — the same fit switchBand performs for bands.

Layout ​

getAvailableLayouts() ​

Returns the layout IDs registered by the active UI build.

javascript
console.log(api.getAvailableLayouts());
// ['panel', 'boutique', 'modern-metals']

Returns: string[]

getLayout() ​

Returns the selected layout ID, or null when the UI should use its default.

Returns: string | null

setLayout(id) ​

Switches to a registered layout and emits layout:changed. Unknown IDs are ignored and produce a validation:warning event.

javascript
api.setLayout('boutique');

Changing layout affects presentation only. It does not add or remove rings. Drive-created Modern Metals projects are single-ring because Drive scaffolds their project state that way.

Returns: void

registerLayouts(ids) ​

Declares the layout ids this UI build provides, so getAvailableLayouts() and setLayout() can validate against them. The bundled React UI calls this for you. A custom front end calls it only when it registers layouts of its own.

javascript
api.registerLayouts(['panel', 'boutique', 'modern-metals']);

Returns: void

Theme ​

setTheme(theme) ​

Apply a preset theme or custom theme configuration.

javascript
// Preset
api.setTheme('dark');

// Custom with preset base
api.setTheme({
  preset: 'luxury-gold',
  colors: { primary: '#8B7355' },
  fonts: {
    body: "'Cormorant Garamond', serif",
    googleFonts: ['Cormorant+Garamond:wght@400;500;600'],
  },
  panelWidth: 380,
  showPoses: true,
  showARButton: true,
});

// Fully custom
api.setTheme({
  colors: {
    primary: '#2E5B3C',
    text: '#1a1a1a',
    background: '#F5F7F5',
    surface: '#ffffff',
    border: '#D4DDD4',
  },
});

See Theming for full details.

Returns: void

cycleTheme() ​

Cycles to the next preset theme. Useful for previewing themes.

javascript
const nextTheme = api.cycleTheme();
console.log(`Now using: ${nextTheme}`);

Returns: ThemePresetName (the name of the newly applied theme)

getThemeCSSVariables() ​

Returns the core CSS custom properties resolved from the active theme. This is useful when styling a specification sheet or host-page controls to match the built-in Wedding Band UI.

javascript
const variables = api.getThemeCSSVariables();
Object.entries(variables).forEach(([name, value]) => {
  document.documentElement.style.setProperty(name, value);
});

Returns: Record<string, string>

Pricing ​

setPricingParams(params, bandName?) ​

Override pricing parameters at runtime.

javascript
api.setPricingParams({
  metalDensityGcm3: 15.5,
  metalPricePerGram: 52.0,
  diamondPricePerCarat: 1500,
  markupMultiplier: 2.5,
  weightUnit: 'gram',           // 'gram' | 'ounce' | 'troy_ounce'
});

Returns: void

See Pricing API for detailed documentation.

Events ​

Subscribe to events using the events property:

javascript
const unsubscribe = api.events.on('price:updated', (data) => {
  console.log(data);
});

// Unsubscribe
unsubscribe();
// or
api.events.off('price:updated', handler);

For direct integrations, wait for the window-level ijewel-viewer-ready event before reading the controller. For cross-origin iframes, wait for the forwarded { event: 'ready' } message. The controller's own ready event is emitted during initialization and may occur before a same-page host obtains the controller.

Event Reference ​

EventPayloadFired When
profile:changed{ bandName, profileIndex, profileName }Profile shape changes
path:changed{ bandName, pathId, strength? }Extrusion path changes
dimensions:changed{ bandName, width, height, radius }Width, height, or radius changes
material:changed{ bandName, slot, metal, finish }Material on any slot changes
partition:changed{ bandName, numColors }Number of color zones changes
diamonds:changed{ bandName, settingType, numStones }Diamond configuration changes
edge:changed{ bandName, type, side }Edge treatment changes
engraving:changed{ bandName, text, font }Engraving text or font changes
milgrain:changed{ bandName, enabled }Milgrain state changes
finish:changed{ bandName, insidePolished, splitGroove }Inside polish or groove split toggles
inlays:changed{ bandName, inlays }The inlay list changes
overlays:changed{ bandName, overlays }The rim-overlay list changes
sleeve:changed{ bandName, sleeve }The bore sleeve changes or clears
build:started{ bandName }Ring geometry rebuild begins
build:complete{ bandName, durationMs }Ring geometry rebuild finishes
price:updated{ bandName, pricing }Price recalculated (see PriceBreakdown)
history:changed{ canUndo, canRedo }Undo or redo availability changes
config:saved{ id, name }A saved configuration is created or replaced
config:loaded{ id, name? }A saved configuration is loaded
config:deleted{ id }A saved configuration is deleted
band:switched{ from, to }Active band changes
link:changed{ linked }Wedding-band linking changes
rings:changed{ rings }Ring compilation changes (add / remove / reorder / rename / metal / visibility)
compatibility:resolved{ bandName, feature, value, action, reason, message }A configuration change made a feature incompatible and it was auto-removed, or an incompatible request was rejected
pose:changed{ poseIndex }Camera pose changes
ar:started{}AR try-on session begins
ar:stopped{}AR try-on session ends
theme:changed{ theme, ui? }Theme or UI options are applied
layout:changed{ from, to }Built-in layout changes
ready{}Plugin fully initialized
disposed{}Plugin disposed and cleaned up
validation:warning{ field, message, provided, corrected }A value was clamped or rejected (out of range input)
error{ source, message, details? }An error occurred (also emitted as a log event with level 'error')
log{ level, source, message, details?, timestamp }Any log entry emitted (all levels)

The cross-origin bridge forwards every event in this table except pose:changed, ar:started, and ar:stopped. Those three events are available only through direct api.events subscriptions.

Lifecycle ​

dispose() ​

Removes controller event listeners and marks the controller unavailable. The Mini Viewer calls this automatically when a Wedding Band project is cleared, reloaded, or the viewer is destroyed. Application code normally should not call it directly.

javascript
api.dispose(); // advanced/manual teardown only

After disposal, get the new controller from the next viewer/project initialization instead of reusing the old instance.

Types ​

RingSnapshot ​

typescript
interface RingSnapshot {
  bandName: string
  profile: ProfileSnapshot
  dimensions: DimensionsSnapshot
  materials: MaterialSnapshot
  diamonds: DiamondSnapshot | null
  edge: EdgeSnapshot
  engraving: EngravingSnapshot | null
  pricing: PriceBreakdown | null
}

ProfileSnapshot ​

typescript
interface ProfileSnapshot {
  index: number
  name: string
  fileName: string
}

DimensionsSnapshot ​

typescript
interface DimensionsSnapshot {
  widthMm: number  // Legacy name: profile width scale multiplier
  heightMm: number // Legacy name: profile height scale multiplier
  radiusMm: number // Physical radius in millimeters
}

MaterialSnapshot ​

typescript
interface MaterialSnapshot {
  partition: number       // Number of active material zones
  slots: MaterialSlot[]
  insidePolished: boolean
  splitAtGroove: boolean
  inlays?: InlayConfig[]
  overlays?: OverlayConfig[]
  sleeve?: SleeveConfig
}

interface MaterialSlot {
  slot: number            // One-based active material slot
  metal: string           // Base material id — same value as `base`
  finish: string          // Finish id; '' for a material with no finish axis
  base?: string           // Base material id
  variant?: string        // Quality or sub-category: '18k', '14k', 'koa'
}

// The same slot on the way in, through batch() / importConfig(): only `slot`
// is required, and an omitted field keeps that slot's current value.
interface MaterialSlotInput extends Partial<MaterialSlot> {
  slot: number
}

MaterialEntry, MaterialVariant, and MaterialRef ​

typescript
type MaterialUsage = 'band' | 'inlay' | 'overlay' | 'sleeve'

interface MaterialEntry {
  id: string
  name: string
  kind?: 'metal' | 'ceramic' | 'organic' | 'composite'
  iconUrl?: string
  swatch?: string
  usage?: MaterialUsage[]
  variants: MaterialVariant[]
  defaultVariant?: string
  finishes?: string[]
  defaultFinish?: string | null
}

interface MaterialVariant {
  id: string
  name: string
  iconUrl?: string
  swatch?: string
  files?: Record<string, string>
  file?: string
  density?: number
  pricePerGram?: number
}

interface MaterialRef {
  base: string
  variant?: string
  finish?: string
}

density and pricePerGram are reserved fields. The current runtime pricing engine does not read per-variant values from them.

InlayConfig ​

typescript
interface InlayConfig {
  centerZ: number
  widthMm: number
  metal: string
  variant?: string
  finish?: string
}

OverlayConfig ​

typescript
interface OverlayConfig {
  side: 'left' | 'right'
  widthMm: number
  metal: string
  variant?: string
  finish?: string
  coversRim?: boolean
  rimCoverage?: number
}

SleeveConfig ​

typescript
interface SleeveConfig {
  enabled: boolean
  metal: string
  variant?: string
  finish?: string
  full?: boolean
  widthMm?: number
}

DiamondSnapshot ​

typescript
interface DiamondSnapshot {
  settingType: string
  span: string
  spacing: string
  count: number
  placement: number
  stoneSize: number
  position: number    // -1 (left) to 1 (right), 0 = center
}

EdgeSnapshot ​

typescript
interface EdgeSnapshot {
  type: string
  side: string
}

EngravingSnapshot ​

typescript
interface EngravingSnapshot {
  text: string
  font: string
  fontSize: number
}

PriceBreakdown ​

typescript
interface PriceBreakdown {
  volumeMm3: number
  weightGrams: number
  metalPrice: {
    name: string            // e.g. "White Gold" - reflects active metal
    densityGcm3: number
    pricePerGram: number
    totalUsd: number
  }
  makingCharge: {
    mode: 'none' | 'percent' | 'per-gram'
    percent: number
    perGram: number
    totalUsd: number
  }
  diamonds: {
    count: number
    totalCarats: number
    pricePerCarat: number
    totalUsd: number
  } | null
  settingCost: {
    costPerStone: number
    count: number
    totalUsd: number
  } | null
  finishSurcharge: {
    name: string            // e.g. "Hammered"
    totalUsd: number
  } | null
  subtotalUsd: number
  markupMultiplier: number
  totalBeforeRounding: number
  totalUsd: number
}

PricingParams ​

typescript
interface PricingParams {
  metalDensityGcm3?: number
  metalPricePerGram?: number
  metalPrices?: Record<string, MetalPriceEntry>
  finishSurcharges?: Record<string, number>
  diamondPricePerCarat?: number
  markupMultiplier?: number
  weightUnit?: WeightUnit
  makingChargeMode?: MakingChargeMode
  makingChargePercent?: number
  makingChargePerGram?: number
  settingCostPerStone?: number
  roundingEnabled?: boolean
  roundingStep?: number
}

interface MetalPriceEntry {
  density: number
  pricePerGram: number
}

type WeightUnit = 'gram' | 'ounce' | 'troy_ounce'
type MakingChargeMode = 'none' | 'percent' | 'per-gram'

See Pricing Engine for detailed documentation on pricing models, per-metal pricing, making charges, and rounding.

BatchConfig ​

typescript
interface BatchConfig {
  profile?: { index: number } | { name: string }
  dimensions?: Partial<DimensionsSnapshot>
  // Slots are MaterialSlotInput, so a `materials` block read straight out of
  // exportConfig() can be handed back unchanged. `sleeve: null` removes the
  // sleeve; omitting the key leaves the current one alone.
  materials?: Omit<Partial<MaterialSnapshot>, 'slots' | 'sleeve'> & {
    slots?: MaterialSlotInput[]
    sleeve?: SleeveConfig | null
  }
  diamonds?: Partial<DiamondSnapshot> | null
  edge?: Partial<EdgeSnapshot>
  engraving?: Partial<EngravingSnapshot> | null
  pricing?: PricingParams
}

UITheme ​

typescript
interface UITheme {
  preset?: ThemePresetName
  colors?: ThemeColors
  fonts?: ThemeFonts
  shapes?: ThemeShapes
  components?: ThemeComponentOverrides
  customCSS?: string
  panelWidth?: number
  showPoses?: boolean
  showARButton?: boolean
  showExportButton?: boolean
  showRingToggle?: boolean
  initialTab?: string
}

ThemeColors ​

typescript
interface ThemeColors {
  primary?: string
  primaryHover?: string
  secondary?: string
  text?: string
  textMuted?: string
  textPlaceholder?: string
  background?: string
  surface?: string
  surfaceHover?: string
  border?: string
  borderLight?: string
  borderHover?: string
}

ThemeFonts ​

typescript
interface ThemeFonts {
  body?: string
  headline?: string
  light?: string
  googleFonts?: string[]  // e.g., ['Inter:wght@300;400;500']
}

ThemePresetName ​

typescript
type ThemePresetName =
  | 'default'
  | 'luxury-gold'
  | 'modern-minimal'
  | 'dark'
  | 'rose-elegant'
  | 'coral-modern'
  | 'fresh-teal'
  | 'minimal-blue'
  | 'warm-beige'
  | 'classic-gold'
  | 'ijewel'

LogLevel ​

typescript
type LogLevel = 'error' | 'warn' | 'info' | 'silent'

LogSource ​

typescript
type LogSource = 'api' | 'material' | 'geometry' | 'ar' | 'config' | 'engraving' | 'pricing'

LogEntry ​

typescript
interface LogEntry {
  level: Exclude<LogLevel, 'silent'>
  source: LogSource
  message: string
  details?: any
  timestamp: number
}

CSS Custom Properties ​

The theme system injects CSS custom properties that you can use in your own stylesheets:

PropertyDescription
--rb-color-primaryPrimary accent color
--rb-color-primary-hoverPrimary hover state
--rb-color-secondarySecondary accent color
--rb-color-textMain text color
--rb-color-text-mutedSecondary text color
--rb-color-backgroundPage background
--rb-color-surfaceCard/panel background
--rb-color-borderBorder color
--rb-font-bodyBody font family
--rb-font-headlineHeadline font family
--rb-radius-mdBorder radius
css
/* Use theme variables in your custom styles */
.my-custom-panel {
  background: var(--rb-color-surface);
  color: var(--rb-color-text);
  border: 1px solid var(--rb-color-border);
  font-family: var(--rb-font-body);
}