@preference-sl/pref-viewer
v2.16.0
Published
Web Component to preview GLTF models with Babylon.js
Keywords
Readme
@preference-sl/pref-viewer
@preference-sl/pref-viewer is a framework-agnostic Web Component for embedding an interactive 2D/3D product viewer in web applications. It renders glTF/GLB models with Babylon.js, supports SVG drawings, localized UI controls, runtime render settings, asset caching, and export flows for GLB, glTF ZIP, and USDZ.
Features
- Native custom elements:
<pref-viewer>,<pref-viewer-2d>,<pref-viewer-3d>,<pref-viewer-dialog>, and<pref-viewer-menu-3d>. - Babylon.js-based 3D visualization for glTF and GLB assets.
- 2D SVG drawing support with pan and zoom controls.
- Unified
<pref-viewer>wrapper that coordinates 2D, 3D, menu, dialog, localization, and loading lifecycle. - Sequential task queue for deterministic config, model, scene, materials, drawing, options, and culture updates.
- Direct URL, base64/data URI, and IndexedDB-backed asset loading.
- File cache validation using size and timestamp metadata.
- Runtime render settings for anti-aliasing, ambient occlusion, IBL, shadows, lighting time of day, and hover highlight.
- Localized UI copy for
en-ENandes-ES. - Export helpers for model, scene, or combined model + scene downloads.
- Type declarations for TypeScript and JSX usage.
Installation
npm install @preference-sl/pref-viewerBasic usage
Import the package once in your application entry point. The import registers the custom elements globally.
import "@preference-sl/pref-viewer";Then use the element in HTML:
<pref-viewer
style="display: block; width: 100%; height: 600px;"
mode="3d"
culture="en-EN"
config='{
"model": {
"storage": {
"url": "https://example.com/assets/model.glb"
},
"visible": true
},
"scene": {
"storage": {
"url": "https://example.com/assets/scene.gltf"
},
"visible": true
}
}'
></pref-viewer>JavaScript API usage
You can configure the viewer declaratively with attributes or imperatively with JavaScript. Prefer the JavaScript API when passing large objects, dynamic values, or user-driven updates.
const viewer = document.querySelector("pref-viewer");
viewer.loadConfig({
model: {
storage: {
url: "https://example.com/assets/model.glb",
},
visible: true,
},
scene: {
storage: {
url: "https://example.com/assets/scene.gltf",
},
visible: true,
},
culture: "en-EN",
});
viewer.setOptions({
camera: "MainCamera",
ibl: {
url: "https://example.com/assets/environment.hdr",
intensity: 1,
shadows: true,
},
lighting: {
timeOfDay: 0.5,
},
});Configuration model
The main config payload can include model, scene, materials, drawing, options, and culture data.
viewer.loadConfig({
model: {
storage: {
url: "https://example.com/model.glb",
},
visible: true,
},
scene: {
storage: {
url: "https://example.com/scene.gltf",
},
visible: true,
},
materials: {
storage: {
url: "https://example.com/materials.glb",
},
},
drawing: {
storage: {
url: "https://example.com/drawing.svg",
},
},
options: {
camera: "MainCamera",
ibl: {
url: "https://example.com/environment.hdr",
intensity: 1,
shadows: true,
},
render: {
antiAliasingEnabled: true,
ambientOcclusionEnabled: true,
iblEnabled: true,
shadowsEnabled: false,
},
},
culture: "en-EN",
});Asset storage descriptors
Storage descriptors are passed through model.storage, scene.storage, materials.storage, or drawing.storage.
Remote URL
{
storage: {
url: "https://example.com/assets/model.glb"
}
}IndexedDB-backed asset
{
storage: {
db: "PrefConfiguratorDB",
table: "gltfModels",
id: "product-model-id",
url: "https://example.com/assets/fallback-model.glb"
}
}Base64 or data URI
{
storage: {
url: "data:model/gltf-binary;base64,BASE64_CONTENT"
}
}Attributes
| Attribute | Description |
| --- | --- |
| config | JSON string with the full viewer configuration. |
| model | JSON string with a model payload. |
| scene | JSON string with a scene/environment payload. |
| materials | JSON string with a materials payload. |
| drawing | JSON string with a 2D drawing payload. |
| options | JSON string with viewer options. |
| culture | UI locale. Supported values: en-EN, es-ES. |
| mode | Viewer mode: 3d or 2d. Defaults to 3d. |
| show-model | Shows or hides the current model. Use true or false. |
| show-scene | Shows or hides the current scene/environment. Use true or false. |
| show-menu | Shows or hides the 3D render settings menu. Defaults to visible. |
| accent-color | Forwarded to the 3D component and menu for theme/accent styling when supported by the internal UI. |
| show-animation-menu | Enables or disables the animation menu when animations are available. |
| highlight-enabled | Enables or disables hover highlight. |
| highlight-color | CSS color used for hover highlight. Defaults to #ff6700. |
Public methods
Loading and configuration
| Method | Description |
| --- | --- |
| loadConfig(config) | Queues a full configuration update. Accepts an object or JSON string. |
| loadModel(model) | Queues a model update. |
| loadScene(scene) | Queues a scene/environment update. |
| loadMaterials(materials) | Queues a materials update. |
| loadDrawing(drawing) | Queues a 2D drawing update. |
| setOptions(options) | Queues camera, material, IBL, lighting, or render option updates. |
| setCulture(culture) | Changes the active UI locale. |
| setMode(mode) | Switches between 2d and 3d. |
Parallel loading helpers
| Method | Description |
| --- | --- |
| loadModelParallel(model) | Loads a model without waiting for the standard task queue. |
| loadSceneParallel(scene) | Loads a scene/environment without waiting for the standard task queue. |
| loadMaterialsParallel(materials) | Loads materials without waiting for the standard task queue. |
Visibility and navigation
| Method | Description |
| --- | --- |
| showModel() / hideModel() | Shows or hides the loaded model. |
| showScene() / hideScene() | Shows or hides the loaded scene/environment. |
| syncVisibility(name, isVisible) | Synchronizes model or scene visibility with the corresponding reflected attribute. |
| zoomCenter() | Centers the 2D drawing view. |
| zoomExtentsAll() | Fits all 2D drawing content in view. |
| zoomIn() / zoomOut() | Controls 2D drawing zoom. |
| focusModel() | Moves focus to the current model interaction target when available. |
| getDebugState() | Returns a 3D runtime debug snapshot when the internal 3D component exposes one. |
Animation controls
| Method | Description |
| --- | --- |
| getAnimations() | Returns available model animations. |
| playAnimation(name, action) | Plays, pauses, stops, or otherwise controls a named animation. |
| setAnimationProgress(name, progress) | Sets animation progress from 0 to 1. |
| setAnimationLoop(name, loop) | Enables or disables looping for a named animation. |
Dialogs and downloads
| Method | Description |
| --- | --- |
| openDialog(title, content, footer) | Opens a viewer dialog. |
| closeDialog() | Closes the active viewer dialog. |
| downloadModelGLB() / downloadModelGLTF() / downloadModelUSDZ() | Exports the current model. |
| downloadSceneGLB() / downloadSceneGLTF() / downloadSceneUSDZ() | Exports the current scene/environment. |
| downloadModelAndSceneGLB() / downloadModelAndSceneGLTF() / downloadModelAndSceneUSDZ() | Exports the combined model and scene. |
Public properties
Setters
These setters are convenience bridges for framework bindings and property-based integration.
| Property | Description |
| --- | --- |
| config | Forwards to loadConfig(value). |
| model | Forwards to loadModel(value). |
| scene | Forwards to loadScene(value). |
| materials | Forwards to loadMaterials(value). |
| drawing | Forwards to loadDrawing(value). |
| options | Forwards to setOptions(value). |
| culture | Forwards to setCulture(value). |
| mode | Forwards to setMode(value). |
| highlightEnabled | Reflects to the highlight-enabled attribute. |
| highlightColor | Reflects to the highlight-color attribute. |
Getters
| Property | Description |
| --- | --- |
| culture | Current resolved locale. |
| isInitialized | Whether the root viewer has completed initialization. |
| isLoaded | Whether the root viewer considers the current load complete. |
| isLoading | Whether a load operation is currently in progress. |
| isMode2D | true when the current mode is 2d. |
| isMode3D | true when the current mode is 3d. |
Events
The component emits custom events so host applications can integrate loading indicators, error states, and viewer controls.
| Event | Description |
| --- | --- |
| scene-loading | A 3D scene/model/material operation has started. |
| scene-loaded | A 3D scene/model/material operation completed. Includes operation details when available. |
| scene-error | A scene operation failed. |
| scene-setting-options | Viewer options are being applied. |
| scene-set-options | Viewer options were applied. |
| model-loading | A parallel model load started. |
| model-loaded | A model container loaded successfully. In the parallel path, this means the independent model load completed. |
| model-error | A parallel model load failed. |
| environment-loaded | The environment/scene container loaded successfully. |
| materials-loading | A parallel materials load started. |
| materials-loaded | A parallel materials load completed. |
| materials-error | A parallel materials load failed. |
| drawing-loading | A 2D drawing load started. |
| drawing-loaded | A 2D drawing load completed. |
| drawing-zoom-changed | The 2D drawing zoom changed. |
| model-first-painted | The first frame for a newly loaded model was painted. |
| prefviewer-animation-changed | A model animation state changed. |
| pref-viewer-menu-3d-apply | The 3D settings menu submitted render-setting changes. |
| scene-load-assets-complete | Performance/progress event emitted after asset-container loading completes. |
| scene-load-merge-complete | Performance/progress event emitted after loaded containers are merged into the scene. |
| scene-load-shaders-complete | Performance/progress event emitted after render/shader work completes. |
| scene-load-container-complete | Performance/progress event emitted after an independent container load completes. |
Example:
const viewer = document.querySelector("pref-viewer");
viewer.addEventListener("scene-loaded", (event) => {
console.log("Viewer scene loaded", event.detail);
});
viewer.addEventListener("scene-error", (event) => {
console.error("Viewer scene failed", event.detail);
});Progressive model loading / eager model path
loadModelParallel(model) is the eager/progressive model-loading API. It bypasses the root task queue and asks the 3D controller to load the model container independently while the existing render loop keeps running.
How it behaves:
- The model payload is parsed from an object or JSON string.
model-loadingis dispatched immediately from<pref-viewer>.- The 3D component calls
loadModelIndependent(model). - The Babylon controller uses
loadContainerIndependent("model", ...). - If the model is not already claimed by a full scene load, it follows the optimistic
parpath: download/prepare the glTF container immediately, then merge it atomically. - If a full scene load already claimed the model container, it follows the
claimedpath to avoid a double-load conflict while still letting the independent path own the replacement. - The previous model is replaced without rebuilding camera-dependent effects during the merge (
releaseEffects: false), reducing gray-frame/flicker risk. - After merge, the controller requests a few render frames and waits for paint before dispatching
model-first-painted. - The returned detail includes
success,error,loadedContainers, andloadSessionIdwhen provided by the payload.
Example:
const loadSessionId = crypto.randomUUID();
viewer.addEventListener("model-first-painted", (event) => {
console.log("Model first painted", event.detail);
});
await viewer.loadModelParallel({
loadSessionId,
storage: {
url: "https://example.com/assets/model.glb",
},
visible: true,
});Important constraints:
- The eager model path requires the internal Babylon scene to already exist. If there is no scene, the controller returns
success: falsewithNo scene available. - Only one independent model load can run at a time. A concurrent call returns
success: falsewithAlready loading model independently. model-loadedmeans the model container load finished;model-first-paintedis the stronger signal for UI handoff because it waits for the browser to paint after the atomic merge.- Use this path when the product model should appear as soon as possible while slower scene/environment/material work continues. Use
loadConfig()when deterministic sequential loading is more important than progressive display.
Render settings
Render settings can be passed through setOptions, initial config, or the built-in 3D menu.
viewer.setOptions({
render: {
antiAliasingEnabled: true,
ambientOcclusionEnabled: true,
iblEnabled: true,
shadowsEnabled: false,
lightingTimeOfDay: 0.5,
highlightEnabled: true,
highlightColor: "#ff6700",
},
});Render settings are persisted in localStorage under the pref-viewer/render-settings key when changed through the runtime settings flow.
Localization
The default locale is en-EN. The viewer currently includes translations for:
en-ENes-ES
<pref-viewer culture="es-ES"></pref-viewer>viewer.setCulture("en-EN");Development
Install dependencies:
npm installRun tests:
npm testStart the local playground:
npm startThe playground is available from playground/index.html and serves the project through http-server after bundling the local source.
Package scripts
| Script | Description |
| --- | --- |
| npm run build | Bundles src/index.js into dist/bundle.js with esbuild. |
| npm run build:prefweb | Creates a production webpack build. |
| npm run build:prefweb:dev | Creates a development webpack build. |
| npm start | Builds the bundle and starts a local HTTP server on port 8080. |
| npm test | Runs the Vitest test suite. |
| npm run release | Creates a standard-version release. |
| npm run release:beta | Creates a beta prerelease with standard-version. |
TypeScript and JSX
The package ships index.d.ts, including JSX support for <pref-viewer>.
export function ProductViewer() {
return (
<pref-viewer
model={JSON.stringify({
storage: {
url: "https://example.com/assets/model.glb",
},
})}
/>
);
}Notes for integrators
- The component is a Web Component, so it can be used from plain HTML, React, Vue, Angular, or any framework that supports custom elements.
- Give the element an explicit width and height. Babylon.js needs a measurable canvas container.
- Use object-based JavaScript APIs for complex payloads. Attributes must be valid JSON strings.
- Remote assets must be reachable by the browser and must satisfy the expected CORS policy.
- glTF files with external resources are resolved and cached by the viewer when possible.
- Avoid repeatedly replacing large assets if only visibility, camera, lighting, or material options changed.
License
Proprietary. Copyright (c) 2026 Preference S.L. All rights reserved.
This package is published for authorized Preference S.L. customers and partners. Use, distribution, and access are governed by the applicable commercial agreement with Preference S.L. See LICENSE.md.
