TSL + Vue Quick Integration Guide
Aimed at developers with Vue 3 + Vite basics. Goal: run Three.js WebGPU/TSL with minimal steps and produce a verifiable animated color plane.
1. Prerequisites
- A WebGPU-capable browser (recent Chrome, etc.). Without WebGPU, Three.js may fall back and TSL features can be limited.
- Use three@latest and import explicitly from three/webgpu and three/tsl.
2. Install
# Install in your Vue project (latest recommended)
npm i three3. Minimal runnable example (split for proper highlighting)
This example uses color、 time、 sin、 positionLocal TSL nodes.
Create: src/components/ColorPlaneTSL.vue
template:
<template>
<div ref="el"></div>
</template>script:
// <script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import * as THREE from 'three/webgpu'
import { color, time, sin, positionLocal } from 'three/tsl'
const el = ref(null)
let renderer, scene, camera, mesh
const handleResize = () => {
if (!renderer || !camera) return
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
onMounted(async () => {
// 1) 场景 / 相机 / 渲染器
scene = new THREE.Scene()
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.z = 2.5
renderer = new THREE.WebGPURenderer({ antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
el.value.appendChild(renderer.domElement)
// 2) 平面网格 + Node 材质(TSL)
const geometry = new THREE.PlaneGeometry(2, 2)
const material = new THREE.MeshBasicNodeMaterial()
// TSL color node equivalent to the R3F example: RGB varying over time
const R = sin(positionLocal.x.add(time)).add(1).mul(0.5)
const G = sin(positionLocal.y.add(time)).add(1).mul(0.5)
const B = sin(positionLocal.x.add(time)).add(1).mul(0.5)
material.colorNode = color(R, G, B)
mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
// 3) 监听尺寸
window.addEventListener('resize', handleResize)
// 4) 初始化并渲染(不同 three 版本:init 可能是函数或 Promise 属性)
if (typeof renderer.init === 'function') await renderer.init()
else if (renderer.init) await renderer.init
renderer.setAnimationLoop(() => {
renderer.render(scene, camera)
})
})
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
renderer?.setAnimationLoop(null)
mesh?.geometry?.dispose?.()
mesh?.material?.dispose?.()
renderer?.dispose?.()
})
// </script>Mount in src/App.vue:
<!-- src/App.vue -->
<template>
<ColorPlaneTSL />
</template>
<script setup>
import ColorPlaneTSL from './components/ColorPlaneTSL.vue'
</script>
<style>
html, body, #app { height: 100%; margin: 0; }
</style>(Optional) Vite config example
// vite.config.js(可选)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'three/addons': 'three/examples/jsm'
}
}
})4. Run & verify
- Start the dev server: npm run dev.
- If you see a time-varying color plane, it works.

5. Troubleshooting
- Black screen or blank: Confirm WebGPU support; ensure you call await renderer.init() (function or Promise-like depending on three version); finally check renderer.domElement is appended to the container.
- Cannot find three/tsl or WebGPURenderer: Upgrade to three@latest. Import renderer and Node materials from three/webgpu, and node functions (color, time, sin, positionLocal) from three/tsl.
- Multiple instances of Three.js: You imported multiple three copies. Unify import paths (use three/webgpu everywhere) and add resolve.alias in Vite to point 'three' to a single version.
- Colors not changing over time: Use MeshBasicNodeMaterial, assign material.colorNode (not material.color), and drive rendering with renderer.setAnimationLoop.
- Aspect ratio broken after resize: On resize, update camera.aspect + camera.updateProjectionMatrix() and call renderer.setSize().