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

@optiklab/web-gpu-visualizer-component

v0.1.6

Published

WebGPU-first 3D OBJ visualizer with a Canvas CPU fallback and React bindings.

Downloads

1,094

Readme

WebGPU Visualizer Component

CI npm license

A dependency-light OBJ visualizer that prefers WebGPU and automatically falls back to a Canvas 2D software renderer. It provides React bindings and a framework-independent controller, with wireframe, filled, and textured rendering.

Component is built from the the previously successful experimental project converting from C++ SDL functionality to TypeScript for renderring using Web GPU and CPU (as a fallback).

Production demo

See demo on TacTicA.

Install

npm install @optiklab/web-gpu-visualizer-component

React 18.2 or newer is required when using the React entry point.

React

import { WebGpuVisualizer } from '@optiklab/web-gpu-visualizer-component/react';
import '@optiklab/web-gpu-visualizer-component/styles.css';

const scene = {
  models: [{
    id: 'product',
    objUrl: '/models/product.obj',
    textureUrl: '/models/product.png',
    translation: { x: 0, y: 0, z: 5 },
  }],
};

export function ProductViewer() {
  return (
    <WebGpuVisualizer
      scene={scene}
      renderMode="textured"
      style={{ width: '100%', height: 480 }}
      onFallback={error => console.info('Using CPU renderer:', error.message)}
      onError={console.error}
    />
  );
}

Each model accepts either objUrl or objText, plus an optional textureUrl, translation, rotation, and scale. Scene and asset URLs remain owned and hosted by the consuming application.

For OBJ files that use multiple materials, provide an MTL source and its diffuse textures:

const scene = {
  models: [{
    id: 'ship',
    objUrl: '/models/ship.obj',
    mtlUrl: '/models/ship.mtl',
    textureUrl: '/models/fallback.png',
  }],
};

Relative map_Kd paths in a hosted MTL file resolve relative to mtlUrl. For local files, pass the MTL text and map each uploaded filename to its blob URL:

const scene = {
  models: [{
    id: 'local-ship',
    objText,
    mtlText,
    textureUrls: {
      'hull.png': hullBlobUrl,
      'glass.jpg': glassBlobUrl,
    },
  }],
};

textureUrls first matches the complete map_Kd path, then its basename, without requiring matching letter case. It also recognizes a unique exporter-added numeric suffix, such as Metal0_2.jpg referring to an uploaded Metal0.jpg. textureUrl remains the fallback for faces without a mapped diffuse texture. Browser-decodable PNG, JPEG, and WebP textures are recommended; TIFF is not supported.

Props

| Prop | Default | Purpose | | --- | --- | --- | | scene | required | Models and transforms to load | | renderer | 'auto' | 'auto', 'webgpu', or 'webcpu' | | fallbackToCpu | true | Use Canvas 2D if WebGPU initialization or rendering fails | | renderMode | 'textured' | 'wireframe', 'filled', or 'textured' | | controls | true | Enable pointer and wheel controls | | keyboard | true | Enable keyboard camera controls | | pixelRatio | device ratio | Render scale, clamped from 0.5 to 3 | | showStatus | true | Show loading, fallback, and error status overlays | | onReady | - | Receives the initialized renderer kind | | onRendererChange | - | Runs whenever the active backend changes | | onFallback | - | Receives the WebGPU error that caused fallback | | onError | - | Receives an unrecoverable loading or rendering error |

A forwarded ref exposes loadScene, setModelTransform, setRenderingPaused, resetCamera, setRenderMode, and getRendererKind. Calling setRenderingPaused(true) cancels the controller's scheduled animation frame; calling it with false starts a fresh frame. Use it around browser transitions that temporarily invalidate the canvas surface, then resume after dimensions stabilize.

Model transforms and animation

Use setModelTransform for frequent translation, rotation, or scale updates. It updates the loaded model in place without reparsing OBJ geometry, decoding textures, or recreating renderer resources.

const visualizerRef = useRef<WebGpuVisualizerHandle>(null);

useEffect(() => {
  let frame = 0;
  const animate = (timestamp: number) => {
    visualizerRef.current?.setModelTransform('product', {
      rotation: { x: 0, y: timestamp / 1000, z: 0 },
    });
    frame = requestAnimationFrame(animate);
  };
  frame = requestAnimationFrame(animate);
  return () => cancelAnimationFrame(frame);
}, []);

Model IDs must be unique within a scene. The method returns false when no loaded model has the requested ID. Use loadScene only when geometry or textures change.

Framework-Independent API

import { WebGpuVisualizer } from '@optiklab/web-gpu-visualizer-component';

const canvas = document.querySelector<HTMLCanvasElement>('#viewer')!;
const visualizer = new WebGpuVisualizer(canvas, {
  scene,
  renderer: 'auto',
  fallbackToCpu: true,
});

await visualizer.initialize();

// Call when the owning view is removed.
visualizer.dispose();

The canvas needs a stable CSS width and height. The controller observes it and maintains the correct backing-buffer resolution.

Renderer Selection

renderer: 'auto' and renderer: 'webgpu' both attempt full WebGPU initialization, including adapter and device creation. If that fails and fallbackToCpu is enabled, the component activates the software renderer. Set renderer: 'webcpu' to force software rendering or fallbackToCpu: false to surface WebGPU failures.

WebGPU availability depends on the browser, operating system, graphics driver, security context, and hardware. The CPU backend keeps the viewer functional without a supported GPU, but complex models will render more slowly.

Controls

  • Left drag: rotate
  • Right drag or Shift-drag: pan
  • Wheel: zoom
  • Double-click: reset
  • Arrow Up/Down: move forward/backward
  • Arrow Left/Right: rotate horizontally
  • W/S: rotate vertically

SSR

The React module does not access browser globals during import or render. In Next.js and similar frameworks, render the component in a client component because initialization requires a canvas. Asset URLs should be absolute or served from the application's public directory.

OBJ and MTL Support

The OBJ parser supports positions, texture coordinates, triangle faces, polygon fan triangulation, positive indices, negative relative indices, and usemtl assignments. The MTL parser supports newmtl and diffuse map_Kd textures, including common map options. Other material properties such as colors, opacity, bump maps, and specular maps are not currently interpreted.

Development

Requires Node.js 20.19 or newer.

Run the component demo

git clone https://github.com/optiklab/web-gpu-visualizer-component.git
cd web-gpu-visualizer-component
npm install
npm run dev

Open http://localhost:5173/?webgpu-check=1 in a browser. The demo starts with the full runway scene and provides selectors for the bundled example models and rendering modes. It attempts WebGPU first and displays CPU fallback when WebGPU is unavailable or initialization fails.

Vite may select another port when 5173 is already occupied. In that case, use the local URL printed by npm run dev and append ?webgpu-check=1.

1

Validate the package

npm run lint
npm test
npm run build
npm run test:package
npm run pack:check

The repository contains model assets for its local demo. Vite excludes the public demo directory from the npm library build, so those aircraft and runway assets are not included in the published package tarball. Consumers are responsible for ensuring they have the right to distribute the models and textures they provide.

Publish to npm

The package is published publicly under the optiklab npm scope. Sign in and confirm the active account without sharing an access token:

npm login
npm whoami

npm whoami must print optiklab. Review the files and metadata that npm will receive, then publish:

npm run pack:check
npm publish --access public

prepublishOnly automatically runs lint, all tests, the production build, and an isolated installation test before npm accepts the package. If the npm account requires two-factor authentication, enter the one-time code directly at npm's prompt.

Verify the published release:

npm view @optiklab/web-gpu-visualizer-component version
npm install @optiklab/web-gpu-visualizer-component

For later releases, update and commit the version first, for example with npm version patch, then push the commit and tag before running npm publish --access public.

License

Apache-2.0. See LICENSE.