Ring Viewer SDK
Integration Guide
JavaScript · Any Framework
TechcoreFor · SDK Documentation

Add an interactive 3D ring viewer to webpage

The Ring Viewer SDK renders a real-time 3D jewelry configurator inside any HTML element. This guide walks you through the complete integration — from loading the script to controlling metal tones, switching models, and cleaning up.

Load Script
Create Container
Initialize
Control
Dispose

Prerequisites

Before you write any code, make sure you have these four credentials from TechcoreFor. They are required for the SDK to load models and render the 3D scene.

Credential
secretKey
Your AES-CBC encryption key in the format {{KEY}} — issued by TCC.
Credential
canvasClientId
Your canvasX runtime license client ID. Used to load the WebGL renderer.
Credential
canvasSecretKey
The secret key paired with your canvasClientId. Never expose this in public source.
Asset
SDK bundle URL
The URL to your bundle-*.js file hosted on S3 or a CDN.
Keep credentials out of source control. Store them in environment variables or a secrets manager — never hardcode them directly in your JavaScript files.

How it works

The SDK bundle, when loaded, attaches a global object called window.techcoreFor to the page. You call techcoreFor.init() with your credentials and a container element. It returns a ring instance — an object with methods to change metal tones, load different ring models, control the camera, and more.

The SDK renders into a WebGL canvas it creates inside your container. You control the size of that canvas entirely through CSS on your container element.

1
Step One

Load the SDK script

Add a <script> tag pointing to your SDK bundle URL. The script attaches the window.techcoreFor global once it finishes loading.

Option A — Static script tag

HTML index.html
<!-- Place before your closing </body> tag -->
<script src="https://your-cdn.example.com/bundle-forevermark.js"></script>
<script>
  window.addEventListener('load', () => {
    const sdk = window.techcoreFor;
    // sdk is ready — proceed to Step 3
  });
</script>

Option B — Dynamic loader (recommended for JS frameworks)

JavaScript load-sdk.js
let _sdkPromise = null;

function loadRingSDK(bundleUrl) {
  if (window.techcoreFor) return Promise.resolve(window.techcoreFor);
  if (_sdkPromise)      return _sdkPromise;

  _sdkPromise = new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src   = bundleUrl;
    script.async = true;
    script.addEventListener('load', () =>
      window.techcoreFor ? resolve(window.techcoreFor)
                           : reject(new Error('techcoreFor not defined after load'))
    );
    script.addEventListener('error', () =>
      reject(new Error(`Failed to load SDK from ${bundleUrl}`))
    );
    document.head.appendChild(script);
  });

  return _sdkPromise;
}
One load, many callers. The module-level _sdkPromise variable ensures the <script> tag is injected only once, even if multiple components call loadRingSDK() simultaneously.
2
Step Two

Create a container element

The SDK renders a WebGL canvas inside whatever element you give it. Create a <div> and size it with CSS — the canvas fills 100% of it.

HTML index.html
<div id="ring-viewer" style="width: 600px; height: 500px;"></div>
Any CSS unit works. Use px, %, vh, or responsive breakpoints. For full-page viewers, width: 100%; height: 100vh works well. No minimum size is imposed.

How the Canvas & GLB work

Understanding this flow helps you size the viewer correctly, serve model files efficiently, and debug loading issues before they reach production.

WebGL Canvas

When you call techcoreFor.init(), the SDK automatically creates a <canvas> element and appends it inside your container. You do not create the canvas yourself — the SDK owns it.

Your container div
SDK-created <canvas> (WebGL)
3D ring rendered here

The canvas always fills 100% of the container's width and height. To resize the viewer, resize the container:

CSS your-styles.css
/* Fixed size */
#ring-viewer { width: 600px; height: 500px; }

/* Responsive — fills the column, fixed height */
#ring-viewer { width: 100%; height: 480px; }

/* Full viewport */
#ring-viewer { width: 100vw; height: 100vh; }
Do not add padding or borders directly to the container. The canvas measures the container's inner dimensions via ResizeObserver. Padding shrinks the usable area; use a wrapper element for visual chrome instead.
GLB Model Files

Each ring model is a .glb file — a binary-encoded glTF 2.0 package that bundles the 3D mesh, PBR materials, and textures into a single download. The SDK fetches the correct .glb from your CDN automatically when you call init() or ring.loadModel().

Catalog lookup
SDK fetches models.json and resolves the model ID to a .glb URL.
GLB download
The binary is fetched from your CDN over HTTPS. Progress fires onLoadProgress(0…1).
WebGL upload
Mesh geometry and textures are uploaded to the GPU. The canvas renders the first frame and onLoad() fires.
Interactive
User can orbit, zoom, and switch metal tones in real time.
CORS must be open on your CDN. The SDK fetches models.json and .glb files from the browser. Set Access-Control-Allow-Origin: * (or your domain) on those responses, otherwise the fetch will be blocked and onError fires.
3
Step Three

Initialize the viewer

Call techcoreFor.init() with your credentials, container, and first model. It returns a Promise that resolves to a ring instance used for all further controls.

JavaScript main.js
const container = document.getElementById('ring-viewer');

const ring = await window.techcoreFor.init({
  // Required
  container:       container,
  secretKey:       'YOUR_KEY_HEX.YOUR_IV_HEX',
  canvasClientId:  'YOUR_CANVAS_CLIENT_ID',
  canvasSecretKey: 'YOUR_CANVAS_SECRET_KEY',
  model:           '15271',
  metalTone:       'WG',  // 'WG' | 'YG' | 'RG'

  // Optional — loading callbacks
  onLoadStart:    () => showSpinner(true),
  onLoadProgress: (p) => setProgress(Math.round(p * 100)),
  onLoad:         () => showSpinner(false),
  onError:        (err) => console.error(err),
});

console.log('Available models:', ring.availableModels);
Check ring.availableModels after init. This is the live list of IDs pulled from your catalog. Always use these IDs when calling ring.loadModel().
4
Step Four

Change the metal tone

Call ring.setMetalTone() to switch between 'WG' (White Gold), 'YG' (Yellow Gold), and 'RG' (Rose Gold).

JavaScript
document.getElementById('btn-white-gold').addEventListener('click', () =>
  ring.setMetalTone('WG')
);
document.getElementById('btn-yellow-gold').addEventListener('click', () =>
  ring.setMetalTone('YG')
);
document.getElementById('btn-rose-gold').addEventListener('click', () =>
  ring.setMetalTone('RG')
);
5
Step Five

Switch between ring models

Call ring.loadModel() with a model ID from ring.availableModels. The current metal tone is preserved automatically.

JavaScript
async function switchModel(modelId) {
  if (!ring.availableModels.includes(modelId)) {
    console.warn(`Model "${modelId}" is not in availableModels`);
    return;
  }
  await ring.loadModel(modelId);
}
Show a loading state. loadModel() is async and can take a moment — set a loading indicator before the await and clear it after.
6
Step Six

Other viewer controls

The ring instance exposes camera views, auto-rotate, and screenshot capture.

JavaScript
// Camera view — 'perspective' | 'front' | 'side'
await ring.setView('perspective');
await ring.setView('front');
await ring.setView('side');

// Auto-rotate
ring.setAutoRotate(true);          // start
ring.setAutoRotate(false);         // stop
ring.setAutoRotate(true, 0.5);     // custom speed

// Screenshot → download as PNG
const blob = await ring.screenshot();
const a    = document.createElement('a');
a.href     = URL.createObjectURL(blob);
a.download = 'ring.png';
a.click();
URL.revokeObjectURL(a.href);
7
Step Seven

Clean up when done

Call ring.dispose() when the viewer is removed from the page. This releases the WebGL context and stops background work, preventing memory leaks.

JavaScript
function teardown() {
  if (ring) { ring.dispose(); ring = null; }
}

// React:   return () => teardown()     (inside useEffect)
// Vue:     beforeUnmount() { teardown() }
// Vanilla: window.addEventListener('beforeunload', teardown)
Always dispose before re-mounting. Skipping this causes multiple WebGL contexts to stack up and can crash the browser tab.

Init options reference

All options accepted by techcoreFor.init().

OptionTypeRequiredDescription
containerElement | stringrequiredDOM element or CSS selector the canvas is rendered into.
secretKeystringrequiredAES-CBC key in {{KEY}} format, issued by TCC.
canvasClientIdstringrequiredcanvasX runtime license client ID.
canvasSecretKeystringrequiredSecret key paired with canvasClientId.
modelstringrequiredFirst model to load — must be a valid ID from your TCC catalog.
metalTone'WG' | 'YG' | 'RG'requiredStarting metal tone.
view'perspective' | 'front' | 'side'optionalInitial camera angle. Defaults to 'perspective'.
baseUrlstringoptionalAsset base URL. Defaults to TCC production CDN.
catalogPathstringoptionalCatalog JSON path relative to baseUrl. Default: "models.json".
renderScalenumberoptionalResolution multiplier. Default 1. Use 0.9 on low-end devices.
cameraobjectoptionalminDistance, maxDistance, maxPolarAngle constraints.
onLoadStart() => voidoptionalCalled when loading begins.
onLoadProgress(p: number) => voidoptionalProgress from 0 to 1.
onLoad() => voidoptionalCalled when model is fully visible.
onError(err: Error) => voidoptionalCalled on init or load failure.

Instance API

Methods and properties on the ring object returned by init().

ring.setMetalTone(tone)
Switch the metal material. Accepts 'WG', 'YG', or 'RG'.
Promise<void>
ring.loadModel(modelId)
Load a different model. Use IDs from ring.availableModels.
Promise<void>
ring.setView(view)
Change camera angle: 'perspective', 'front', or 'side'.
Promise<void>
ring.setAutoRotate(enabled, speed?)
Enable/disable auto-spin. Optional speed multiplier.
void
ring.screenshot()
Capture the current canvas as a PNG Blob.
Promise<Blob>
ring.dispose()
Destroy the viewer and release all WebGL resources.
void
ring.availableModels
Read-only array of model IDs from your catalog.
string[]
ring.metalTone
Currently active metal tone.
'WG'|'YG'|'RG'
ring.model
Currently loaded model ID.
string
ring.view
Currently active camera view.
string

Complete minimal example

A self-contained HTML page — replace the placeholder credentials with your own.

HTML complete-example.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Ring Viewer</title>
  <style>
    body      { margin:0; background:#111; color:#fff; font-family:sans-serif; }
    #viewer   { width:100vw; height:70vh; }
    .controls { display:flex; gap:12px; padding:16px; flex-wrap:wrap; }
    button    { padding:10px 20px; border:none; border-radius:3px; cursor:pointer;
                background:#222; color:#fff; font-size:14px; }
    button:hover { background:#333; }
    #status   { padding:8px 16px; font-size:13px; color:#888; }
  </style>
</head>
<body>

<div id="viewer"></div>
<div id="status">Loading…</div>
<div class="controls">
  <button id="wg">⬜ White Gold</button>
  <button id="yg">🟡 Yellow Gold</button>
  <button id="rg">🔴 Rose Gold</button>
  <button id="rotate">↻ Auto-Rotate</button>
  <button id="shot">📷 Screenshot</button>
</div>

<script src="https://your-cdn.example.com/bundle-forevermark.js"></script>
<script>
let ring = null, rotating = false;
const status = document.getElementById('status');

window.addEventListener('load', async () => {
  try {
    ring = await window.techcoreFor.init({
      container:       document.getElementById('viewer'),
      secretKey:       'XXXXXXXXxx.XXXXXXxxxx',
      canvasClientId:  'XXXXXXXXXXXXXxxxxxxxXXXXXXXXXXXX',
      canvasSecretKey: 'XXXXXXXXXXXXXxxxxxxxXXXXXXXXXXXX',
      model:           '15271',
      metalTone:       'WG',
      onLoadStart:     () => { status.textContent = 'Loading model…'; },
      onLoad:          () => { status.textContent = 'Ready ✓'; },
      onError:         (err) => { status.textContent = 'Error: ' + err.message; },
    });

    document.getElementById('wg').onclick     = () => ring.setMetalTone('WG');
    document.getElementById('yg').onclick     = () => ring.setMetalTone('YG');
    document.getElementById('rg').onclick     = () => ring.setMetalTone('RG');
    document.getElementById('rotate').onclick = () =>
      ring.setAutoRotate(rotating = !rotating);
    document.getElementById('shot').onclick   = async () => {
      const blob = await ring.screenshot();
      const a = document.createElement('a');
      a.href = URL.createObjectURL(blob);
      a.download = 'ring.png';
      a.click();
      URL.revokeObjectURL(a.href);
    };
  } catch (err) {
    status.textContent = 'Init failed: ' + err.message;
  }
});

window.addEventListener('beforeunload', () => ring?.dispose());
</script>
</body>
</html>