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

@virtualdisplay.io/client

v4.0.0-alpha.7

Published

TypeScript-first 3D model integration library with iframe-based architecture

Readme

Virtualdisplay client

TypeScript library for embedding interactive 3D product models in web applications.

  • Built-in viewer UI with loading indicator, product controls, and action bar
  • Simple attribute-based API for product configurators
  • Server-driven configuration, no client-side mapping needed
  • Fire-and-forget messaging with automatic state synchronisation
  • Zero external dependencies (all bundled)
  • Works with any frontend framework or vanilla JavaScript

Installation

npm install @virtualdisplay.io/client
# or
pnpm add @virtualdisplay.io/client
# or
yarn add @virtualdisplay.io/client

Quick start

Simple product (no options)

import { VirtualdisplayClient } from '@virtualdisplay.io/client';

// Your 3D model is now displayed
const client = new VirtualdisplayClient({
  parent: '#product-container',
  license: 'your-license-key',
  model: 'statue-model',
});

Configurable product (with options)

import { VirtualdisplayClient } from '@virtualdisplay.io/client';

const client = new VirtualdisplayClient({
  parent: '#product-container',
  license: 'your-license-key',
  model: 'sneaker',
});

// Select a product option. The 3D model updates automatically.
// Calls are buffered until the server provides the attribute mapping
client.select('Color', 'red');

The server defines which attributes and values are available for each model. The client receives this configuration automatically via postMessage. No manual mapping required.

How it works

The 3D server hosts the viewer in an iframe, keeping WebGL complexity isolated from your application. The client library handles all communication via postMessage.

Architecture overview

On startup, the server sends the client its configuration (viewer settings, camera limits) and available product attributes. The loading indicator shows progress while the model downloads.

Startup sequence

  1. Client creates iframe; outgoing messages are queued until the server is ready.
  2. Server sends HANDSHAKE when its message listener is active; client flushes queue.
  3. Server sends progress updates while the model downloads. The client displays these in the loading indicator.
  4. Server sends CONFIG with viewer settings and camera limits from model metadata.
  5. Server sends MAPPING with attribute definitions, values, and defaults. onReady fires.

Product configuration

Configure product options by selecting attribute values. The server defines which attributes exist (e.g. "Color", "Size") and which values each has (e.g. "red", "blue"). You select values; the 3D model updates automatically.

Product configuration sequence

  • select() returns immediately; the result arrives asynchronously.
  • Each attribute in the result has its own success flag and optional error.
  • Attributes within the same component fail as a group.

Selecting values

// Select a product option. The 3D model updates automatically.
client.select('Color', 'blue');
client.select('Size', 'large');

Can be called immediately after construction. Selections are buffered until the server handshake completes.

Tracking changes

Keep your UI in sync with the 3D server state:

client.onChange('Color', (selectedValue) => {
  // selectedValue.value is the selected value (e.g. 'blue')
  // selectedValue.isSelected is true
  updateColorButton(selectedValue.value);
});

Querying available values

// All values for an attribute
const colors = client.getValues('Color');
colors.forEach((av) => {
  console.log(av.value, av.isSelected);
});

// A specific value
const blue = client.getValue('Color', 'blue');
if (blue?.isSelected) {
  highlightButton('blue');
}

Direct attribute access

For advanced use cases, get the Attribute instance directly:

const colorAttr = client.getAttribute('Color');
if (colorAttr) {
  colorAttr.select('blue');
  colorAttr.getValues(); // AttributeValue[]
  colorAttr.getValue('blue'); // AttributeValue | undefined
  colorAttr.onChange((value) => { ... });
}

Handling failed mutations

client.onMutationFailed(({ attribute, value }) => {
  console.error(`Mutation failed: ${attribute} = ${value}`);
});

Snapshots

Capture the current 3D view as an image. Useful for product shots, social sharing, or saving configurations.

Snapshot sequence

  • Filename is validated client-side (extension, no path traversal).
  • Server always responds, either with image data or an error reason.

Taking a snapshot

const photo = client.snapshot.take('product-hero-shot.png');

// Register callback for when image is ready
photo.onDeveloped((photoData) => {
  // photoData.filename - The filename you provided
  // photoData.data - Base64 encoded image data
  const img = document.createElement('img');
  img.src = photoData.data;
  document.body.appendChild(img);
});

// Handle errors (e.g., canvas not available)
photo.onError((error) => {
  console.error('Snapshot failed:', error.message);
});

Filename must end with .png. The server always returns PNG data.

Camera

The standard interface includes built-in camera controls when enabled by the operator in the portal. The camera can also be controlled programmatically.

Camera sequence

  • Commands accumulate via chaining; .set() sends them all at once.
  • .reset() sends immediately without chaining.
  • Position updates are continuous. They fire on both programmatic and user-driven camera movement.

Controlling the camera

// Chain commands, then send with .set()
client.camera.rotate(45).set();
client.camera.rotate(90).tilt(45).zoom(150).set();

// Reset to the default position configured in the portal (sends immediately)
client.camera.reset();

Tracking camera position

The current camera position is updated in real-time as the user orbits, zooms, or tilts the model.

client.cameraPosition.onChange((position) => {
  console.log(position.rotation, position.tilt, position.zoom);
});

// Read current values directly
console.log(client.cameraPosition.rotation);
console.log(client.cameraPosition.tilt);
console.log(client.cameraPosition.zoom);

Standard interface

The client renders a complete viewer UI automatically: loading indicator, attribute controls, camera controls, and an action bar (AR, fullscreen).

Which controls are visible is configured by the model operator in the Virtualdisplay portal. The operator decides how the viewer looks and behaves, not the developer integrating the code. This way the operator can match the viewer to their customers' expectations: enabling AR for mobile visitors, choosing which product attributes to expose, or setting the default camera angle and language.

Lifecycle

Ready state

Use onReady to know when the model and attribute definitions are available. Not required before calling select() or onChange(), those are buffered automatically.

client.onReady(() => {
  const values = client.getValues('Color');
  console.log('Available colours:', values.map(v => v.value));
});

Cleanup

client.destroy();

Constructor options

interface ClientOptions {
  parent: string | HTMLElement; // Where to embed the 3D viewer
  license: string; // License key from your Virtualdisplay account
  model: string; // Which 3D model to load
  debug?: boolean; // Shows console logs for troubleshooting
  language?: string; // Match the viewer language to the page: 'nl', 'en', or 'de'
}

Error codes

The client throws VirtualdisplayError for known error conditions:

| Code | Meaning | | --------------------- | ------------------------------------------------ | | NO_MAPPING | Attribute data not yet available from the server | | ATTRIBUTE_NOT_FOUND | Requested attribute doesn't exist in the mapping | | VALUE_NOT_FOUND | Requested value doesn't exist for attribute | | PARENT_NOT_FOUND | Parent element for iframe not found | | INVALID_PARAMETER | Invalid parameter (e.g. snapshot filename) | | SNAPSHOT_FAILED | Server could not capture the snapshot |

Troubleshooting

Iframe not loading:

  • Check that the parent element exists in the DOM
  • Verify your license key is valid
  • Ensure the model ID matches your licence

Attributes not working:

  • Verify attribute names match exactly (case-sensitive)
  • Check that the value exists for the attribute
  • Use onMutationFailed to catch server-side errors

Changelog

See the npm version history for release notes.