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

vue-vrm

v0.1.1

Published

Vrm viewer component for Vue 3.

Readme

vue-vrm

English | 日本語

jsdelivr CDN NPM Downloads npm version Open in unpkg

Vue 3 component library for rendering VRM avatars with three.js. It supports VRM 1.0 loading, VRMA playback, camera controls, screenshots, and low-level helper utilities for working with VRM and VRMA buffers.

Features

  • Vue 3 component for rendering VRM 1.0 models from ArrayBuffer
  • VRMA playback with single or multiple animation clips
  • Configurable camera, lights, background, grid, and optional shader pass
  • Optional orbit-style camera interaction with rotate, pan, zoom, and roll
  • Exposed methods for playback control, camera reset, and screenshots
  • Utility exports for validating and loading VRM / VRMA assets outside the component

[!IMPORTANT] This library is designed to accept only an ArrayBuffer instead of a file path to prevent VRM/VRMA files from appearing in source code, source maps, or browser network logs, thus violating license restrictions.

Store files on a object storage (e.g., Amazon S3, Cloudflare R2) and retrieve dynamically via API calls (such as VRoid Hub API).

Requirements

  • Node.js >=24
  • Vue >=3.5
  • three >=0.184
  • @pixiv/three-vrm >=3.5
  • @pixiv/three-vrm-animation >=3.5

Installation

pnpm add vue-vrm vue three @pixiv/three-vrm @pixiv/three-vrm-animation

Quick Start

<script setup lang="ts">
import { ref } from 'vue';
import { VrmCanvas } from 'vue-vrm';

const modelData = ref<ArrayBuffer | null>(null);

async function onModelFile(event: Event): Promise<void> {
  const file = (event.target as HTMLInputElement).files?.[0];
  if (!file) return;
  modelData.value = await file.arrayBuffer();
}
</script>

<template>
  <div>
    <input type="file" accept=".vrm,.glb" @change="onModelFile" />
    <VrmCanvas :model-data="modelData" :show-grid="true" />
  </div>
</template>

Component Example

<script setup lang="ts">
import { computed, ref } from 'vue';
import { resetVrmCanvasCamera, type VrmCanvasExposed, VrmCanvas } from 'vue-vrm';

const modelData = ref<ArrayBuffer | null>(null);
const animationData = ref<ArrayBuffer | null>(null);
const canvasRef = ref<VrmCanvasExposed | null>(null);

const cameraInteraction = computed(() => ({
  enabled: true,
  rotate: true,
  pan: true,
  zoom: true,
  roll: true,
  damping: true
}));

async function loadFile(event: Event, target: 'model' | 'animation'): Promise<void> {
  const file = (event.target as HTMLInputElement).files?.[0];
  if (!file) return;
  const buffer = await file.arrayBuffer();

  if (target === 'model') {
    modelData.value = buffer;
    return;
  }

  animationData.value = buffer;
}

function resetCamera(): void {
  resetVrmCanvasCamera(canvasRef.value);
}
</script>

<template>
  <fieldset>
    <legend>VRM Demo</legend>
    <input type="file" accept=".vrm,.glb" @change="event => loadFile(event, 'model')" />
    <input type="file" accept=".vrma,.glb" @change="event => loadFile(event, 'animation')" />

    <button type="button" @click="resetCamera">Reset camera</button>

    <VrmCanvas
      ref="canvasRef"
      :model-data="modelData"
      :animation-data="animationData"
      :show-grid="true"
      :camera-interaction="cameraInteraction"
      :directional-light="{ color: '#ffffff', intensity: 1, position: [1, 1, 1] }"
      @model:loaded="() => console.log('model loaded')"
      @animation:start="() => console.log('animation started')"
      @error="error => console.error(error)"
    />
  </fieldset>
</template>

Implementation Examples

For real-world usage patterns, see the following examples (Both use Cloudflare Workers):

Props

| Prop | Type | Default | Description | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | modelData | ArrayBuffer \| null | null | VRM 1.0 model buffer. Setting a new value unloads the current model and loads the new one. | | animationData | ArrayBuffer \| ArrayBuffer[] \| null | null | VRMA buffer or multiple VRMA buffers to play on the loaded model. | | animationWeights | number[] \| null | null | Blend weights for multiple animations. If omitted or invalid, weights are split equally. | | loop | boolean | true | Whether animation playback repeats. | | bgTransparent | boolean | false | Renders with a transparent background. | | bgImage | string \| null | null | Background texture URL. Ignored when bgTransparent is true. | | showGrid | boolean | false | Shows a THREE.GridHelper. | | width | number | 480 | Base canvas width in pixels. | | maxWidth | number \| null | null | Caps the rendered width when the container is wider. | | aspectRatio | number | 3 / 4 | Canvas aspect ratio as width / height. | | cameraOptions | { fov?: number; near?: number; far?: number } | { fov: 30, near: 0.1, far: 100 } | Perspective camera settings. | | cameraDistance | number \| null | null | Overrides the automatically computed camera distance. | | cameraEuler | [number, number, number] | [0, 0, 0] | Camera rotation as Euler angles in radians. | | cameraOffset | [number, number, number] | [0, 0, 0] | Offset applied to the camera target. | | cameraLookAt | [number, number, number] | [0, 0.9, 0] | Initial point the camera looks at. | | ambientLight | { color: string; intensity: number } | { color: '#ffffff', intensity: 0.5 } | Ambient light configuration. | | directionalLight | { color: string; intensity: number; position: [number, number, number] } | { color: '#ffffff', intensity: 1, position: [1, 1, 1] } | Directional light configuration. | | shaderPass | ShaderPass \| null | null | Optional postprocessing shader pass added to an EffectComposer. | | cameraInteraction | { enabled?: boolean; rotate?: boolean; pan?: boolean; zoom?: boolean; roll?: boolean; rotateSpeed?: number; panSpeed?: number; zoomSpeed?: number; damping?: boolean; dampingFactor?: number; minDistance?: number; maxDistance?: number; rollSpeed?: number } \| null | null | Enables orbit-style camera interaction. Roll uses the Q / E keys while the canvas is focused. |

Events

| Event | Payload | Description | | -------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | model:loading | none | Fired before a model starts loading. | | model:loaded | VRM | Fired after a model has loaded and been added to the scene. | | model:unloaded | none | Fired after the previous model has been removed. | | model:error | Error | Fired when model validation or loading fails. | | animation:loading | none | Fired before animation loading begins. | | animation:loaded | VRMAnimation \| VRMAnimation[] | Fired when animation data has been parsed and attached to the mixer. | | animation:start | none | Fired after playback starts. | | animation:pause | none | Fired when playback is paused through the exposed API. | | animation:resume | none | Fired when playback resumes through the exposed API. | | animation:stop | none | Fired when playback is stopped through the exposed API. | | animation:end | none | Fired when a non-looping animation reaches the end. | | animation:error | Error | Fired when animation validation or loading fails. | | camera:change | { position: THREE.Vector3; lookAt: THREE.Vector3; distance: number } | Fired when the active camera transform changes. | | camera:options-change | { fov?: number; near?: number; far?: number } | Fired when cameraOptions changes. | | light:ambient-change | { color: string; intensity: number } | Fired when ambient light props change. | | light:directional-change | { color: string; intensity: number; position: THREE.Vector3 } | Fired when directional light props change. | | error | Error | Fired for component-level runtime errors such as background texture loading failures. |

Exposed Component Methods

Use a template ref to access the component instance.

| Method | Return type | Description | | ------------------------------------ | --------------------------------- | ----------------------------------------------------- | | playAnimation() | void | Starts or restarts animation playback. | | pauseAnimation() | void | Pauses the active animation. | | resumeAnimation() | void | Resumes a paused animation. | | stopAnimation() | void | Stops playback and rewinds to the beginning. | | resetCamera() | void | Resets the camera to the initial fitted pose. | | resetCameraPose() | void | Alias of resetCamera(). Included for compatibility. | | captureScreenshot(format?: string) | Promise<string> | Returns a data URL of the current frame. | | getScene() | THREE.Scene \| null | Returns the internal scene instance. | | getCamera() | THREE.PerspectiveCamera \| null | Returns the internal camera instance. | | getRenderer() | THREE.WebGLRenderer \| null | Returns the internal renderer instance. | | getMixer() | THREE.AnimationMixer \| null | Returns the current mixer, if any. | | getVrm() | VRM \| null | Returns the loaded VRM instance. | | getCanvas() | HTMLCanvasElement \| null | Returns the internal canvas element. |

Utility Exports

The package also exports low-level helpers for non-component use cases.

import {
  autoPositionY,
  createMixerWithClips,
  disposeMixer,
  disposeVrm,
  loadVRMAnimation,
  loadVrm,
  meta,
  resetVrmCanvasCamera,
  validateVrm,
  validateVrma
} from 'vue-vrm';

| Export | Description | | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | resetVrmCanvasCamera(instance) | Safely resets a VrmCanvas template ref, with compatibility for both resetCamera() and resetCameraPose(). | | loadVrm(buffer) | Validates and loads a VRM 1.0 model from an ArrayBuffer. | | autoPositionY(vrm) | Moves the loaded VRM so its feet sit on y = 0. | | disposeVrm(vrm) | Disposes the VRM scene and GPU resources. | | loadVRMAnimation(buffer) | Validates and loads a VRMA clip from an ArrayBuffer. | | createMixerWithClips(vrm, animations, weights?) | Creates a THREE.AnimationMixer with optional blend weights. | | disposeMixer(mixer) | Stops and uncaches the mixer root. | | validateVrm(buffer) | Throws if the buffer is not a supported VRM 1.0 GLB. | | validateVrma(buffer) | Throws if the buffer is not a VRMA GLB. | | meta | Build metadata containing the library version and build date. |

Notes

  • Only VRM 1.0 is supported. VRM 0.x buffers are rejected during validation.
  • animationWeights must not sum to more than 1.0.
  • cameraInteraction.roll works with the Q and E keys when the canvas has focus.
  • Passing null to modelData unloads the current model.

Chunking Recommendation

When bundling with Vite or another Rollup-based tool, splitting three and the VRM packages into separate chunks improves cacheability and initial bundle loading behavior.

import type { UserConfig } from 'vite';

const config: UserConfig = {
  build: {
    rollupOptions: {
      output: {
        manualChunks(id: string) {
          if (id.includes('/node_modules/three/')) {
            return 'three';
          }

          if (
            id.includes('/node_modules/@pixiv/three-vrm') ||
            id.includes('/node_modules/@pixiv/three-vrm-animation')
          ) {
            return 'three-vrm';
          }

          return 'vendor';
        }
      }
    }
  }
};

export default config;

Development

This project uses the Rspack stack, not Vite.

pnpm install
pnpm lint
pnpm test
pnpm build

For the demo app:

pnpm dev
pnpm preview

License

© 2026 Logue. Licensed under the MIT License.

🎨 Crafted for Developers

This template is built with a focus on UI/UX excellence and modern developer experience. Maintaining it involves constant testing and updates to ensure everything works seamlessly.

If you appreciate the attention to detail in this project, a small sponsorship would go a long way in supporting my work across the Vue.js and Metaverse ecosystems.

GitHub Sponsors