Resolving Proxy Conflicts Between Vue 3 Reactivity and Three.js

Proxy Interference with Three.js Rendering

When using Vue 3's reactive() to wrap Three.js objects like scene, camera, renderer, and controls, a runtime error occurs during rendering:

three.module.js:24471 Uncaught TypeError: 
'get' on proxy: property 'modelViewMatrix' is a read-only and 
non-configurable data property on the proxy target but the proxy did not 
return its actual value (expected '#<Matrix4>' but got '[object Object]')
at renderObject (three.module.js:24471)
at renderObjects (three.module.js:24458)
at Proxy.WebGLRenderer.render (three.module.js:24258)
at animate (Component.vue:192)

Root Cause: Vue 3 Proxy vs. Three.js Internal Properties

Vue 3's reactivity system relies on JavaScript Proxies to intercept property access. Three.js internally defines numerous read-only properties (e.g., modelViewMatrix, normalMatrix) using Object.defineProperty with configurable: false. When a Proxy intercepts access to such non-configurable properties and returns a value different from the original, JavaScript throws a TypeError.

This behavior is demonstrated by the following example:

const handler = {
  get(target, prop) {
    return 'intercepted value';
  }
};

const originalObj = {};
Object.defineProperty(originalObj, 'readOnlyProp', {
  configurable: false,
  value: 'original value'
});

const proxyObj = new Proxy(originalObj, handler);
console.log(proxyObj.readOnlyProp); // Throws TypeError

Recommended Implementation Patterns

  1. Avoid Reactivity for Core Three.js Objects: Declare fundamental Three.js objects (scene, camera, renderer) using standard variablse instead of Vue's reactive wrappers.

    // Use standard variables for core objects
    let scene, camera, renderer;
    
    function initThree() {
      scene = new THREE.Scene();
      camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
      renderer = new THREE.WebGLRenderer();
    }
    
  2. Selective Reactivity with Raw Conversion: For Vue-controlled objects that need to be added to the scene, use ref() and convert to raw objects when interacting with Three.js.

    import { ref, toRaw } from 'vue';
    
    const meshRef = ref(new THREE.Mesh(geometry, material));
    
    // Add to scene using raw object
    scene.add(toRaw(meshRef.value));
    
    // Render using raw objects
    renderer.render(toRaw(scene), camera);
    

This approach maintains Vue's reactivity for application state while preventing proxy interference with Three.js's internal rendering logic.

Tags: Vue 3 Three.js Proxy Reactivity WebGL

Posted on Sat, 09 May 2026 14:18:32 +0000 by spainsoccerfreak