Skip to content

Latest commit

 

History

29 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vue-vrm

English | 日本語

vue-vrm

npm version NPM Downloads jsdelivr CDN Open in unpkg Open in Gitpod X Follow GitHub Sponsors

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

  • Vue >=3.5
  • three >=0.185
  • @pixiv/three-vrm 3.5.5
  • @pixiv/three-vrm-animation 3.5.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.

Releases

Packages

Used by

Contributors

Languages