npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@preference-sl/pref-viewer

v2.16.0

Published

Web Component to preview GLTF models with Babylon.js

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-EN and es-ES.
  • Export helpers for model, scene, or combined model + scene downloads.
  • Type declarations for TypeScript and JSX usage.

Installation

npm install @preference-sl/pref-viewer

Basic 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:

  1. The model payload is parsed from an object or JSON string.
  2. model-loading is dispatched immediately from <pref-viewer>.
  3. The 3D component calls loadModelIndependent(model).
  4. The Babylon controller uses loadContainerIndependent("model", ...).
  5. If the model is not already claimed by a full scene load, it follows the optimistic par path: download/prepare the glTF container immediately, then merge it atomically.
  6. If a full scene load already claimed the model container, it follows the claimed path to avoid a double-load conflict while still letting the independent path own the replacement.
  7. The previous model is replaced without rebuilding camera-dependent effects during the merge (releaseEffects: false), reducing gray-frame/flicker risk.
  8. After merge, the controller requests a few render frames and waits for paint before dispatching model-first-painted.
  9. The returned detail includes success, error, loadedContainers, and loadSessionId when 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: false with No scene available.
  • Only one independent model load can run at a time. A concurrent call returns success: false with Already loading model independently.
  • model-loaded means the model container load finished; model-first-painted is 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-EN
  • es-ES
<pref-viewer culture="es-ES"></pref-viewer>
viewer.setCulture("en-EN");

Development

Install dependencies:

npm install

Run tests:

npm test

Start the local playground:

npm start

The 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.