TresJS (Vue)
Integrate Makio MeshLine into your TresJS Vue 3 project.
WARNING
TresJS WebGPU support is still experimental. Check the TresJS documentation for the latest status on WebGPU renderer support.
Installation
bash
pnpm add makio-meshline @tresjs/core threeBasic Usage with <primitive>
The simplest approach — create a MeshLine instance in a composable and inject it via <primitive>:
vue
<script setup>
import { onMounted, onUnmounted, shallowRef } from 'vue'
import { MeshLine, circlePositions } from 'makio-meshline'
const lineRef = shallowRef()
onMounted( () => {
const line = new MeshLine()
.lines( circlePositions( 64, 2 ) )
.closed( true )
.lineWidth( 0.1 )
.color( 0xff8800 )
.gradientColor( 0xffffff )
.build()
lineRef.value = line
} )
onUnmounted( () => {
lineRef.value?.dispose()
} )
</script>
<template>
<TresCanvas>
<TresPerspectiveCamera :position="[0, 0, 10]" />
<primitive v-if="lineRef" :object="lineRef" />
</TresCanvas>
</template>Composable: useMeshLine
Create a reusable composable for common patterns:
js
// composables/useMeshLine.js
import { shallowRef, onUnmounted, watch } from 'vue'
import { MeshLine } from 'makio-meshline'
export function useMeshLine( options = {} ) {
const line = shallowRef( null )
function create( opts ) {
// Dispose previous
line.value?.dispose()
const ml = new MeshLine()
if ( opts.points ) ml.lines( opts.points )
if ( opts.closed ) ml.closed( opts.closed )
if ( opts.lineWidth ) ml.lineWidth( opts.lineWidth )
if ( opts.color != null ) ml.color( opts.color )
if ( opts.gradientColor ) ml.gradientColor( opts.gradientColor )
if ( opts.dash ) ml.dash( opts.dash )
if ( opts.sizeAttenuation != null ) ml.sizeAttenuation( opts.sizeAttenuation )
if ( opts.transparent || ( opts.opacity != null && opts.opacity < 1 ) ) {
ml.transparent( true )
if ( opts.opacity != null ) ml.opacity( opts.opacity )
}
if ( opts.map ) ml.map( opts.map )
ml.build()
line.value = ml
return ml
}
onUnmounted( () => {
line.value?.dispose()
} )
// Auto-create if options provided
if ( options.points ) create( options )
return { line, create }
}Usage:
vue
<script setup>
import { useMeshLine } from './composables/useMeshLine'
import { circlePositions } from 'makio-meshline'
const { line } = useMeshLine( {
points: circlePositions( 64, 3 ),
closed: true,
lineWidth: 0.2,
color: 0xff0000,
gradientColor: 0x0000ff,
} )
</script>
<template>
<primitive v-if="line" :object="line" />
</template>Reactive Props
Use Vue watch to react to prop changes:
vue
<script setup>
import { ref, watch, onMounted, onUnmounted, shallowRef } from 'vue'
import { MeshLine, circlePositions } from 'makio-meshline'
const props = defineProps( {
radius: { type: Number, default: 2 },
color: { type: Number, default: 0xffffff },
width: { type: Number, default: 0.1 },
} )
const lineRef = shallowRef()
onMounted( () => {
lineRef.value = new MeshLine()
.lines( circlePositions( 64, props.radius ) )
.closed( true )
.lineWidth( props.width )
.color( props.color )
.build()
} )
// Update color and width reactively (no rebuild needed)
watch( () => props.color, ( val ) => lineRef.value?.color( val ) )
watch( () => props.width, ( val ) => lineRef.value?.lineWidth( val ) )
// Rebuild on radius change (geometry changes)
watch( () => props.radius, ( val ) => {
lineRef.value?.lines( circlePositions( 64, val ) ).build()
} )
onUnmounted( () => lineRef.value?.dispose() )
</script>
<template>
<primitive v-if="lineRef" :object="lineRef" />
</template>Dynamic Positions
Animate positions using TresJS useRenderLoop:
vue
<script setup>
import { onMounted, onUnmounted, shallowRef } from 'vue'
import { useRenderLoop } from '@tresjs/core'
import { MeshLine } from 'makio-meshline'
const lineRef = shallowRef()
const points = new Float32Array( 100 * 3 )
onMounted( () => {
lineRef.value = new MeshLine()
.lines( points )
.lineWidth( 0.1 )
.color( 0x44aaff )
.build()
} )
const { onLoop } = useRenderLoop()
onLoop( ( { elapsed } ) => {
for ( let i = 0; i < 100; i++ ) {
const x = ( i / 99 - 0.5 ) * 10
points[i * 3] = x
points[i * 3 + 1] = Math.sin( x + elapsed * 2 ) * 0.5
points[i * 3 + 2] = 0
}
lineRef.value?.geometry.setPositions( points )
} )
onUnmounted( () => lineRef.value?.dispose() )
</script>
<template>
<primitive v-if="lineRef" :object="lineRef" />
</template>TSL Hooks
Apply GPU hooks for custom shader effects:
vue
<script setup>
import { onMounted, onUnmounted, shallowRef } from 'vue'
import { MeshLine, circlePositions } from 'makio-meshline'
import { Fn, sin, time, vec3, smoothstep } from 'three/tsl'
const lineRef = shallowRef()
onMounted( () => {
lineRef.value = new MeshLine()
.lines( circlePositions( 128, 3 ) )
.closed( true )
.lineWidth( 0.3 )
.transparent( true )
// Pulsating width
.widthFn( Fn( ( [w, progress] ) =>
w.mul( sin( time.add( progress.mul( 10 ) ) ).mul( 0.5 ).add( 1 ) )
) )
// Rainbow color
.colorFn( Fn( ( [color, progress] ) => {
const h = progress.mul( 6.28 ).add( time )
return vec3(
sin( h ).mul( 0.5 ).add( 0.5 ),
sin( h.add( 2.09 ) ).mul( 0.5 ).add( 0.5 ),
sin( h.add( 4.18 ) ).mul( 0.5 ).add( 0.5 )
)
} ) )
// Fade ends
.opacityFn( Fn( ( [alpha, progress] ) =>
alpha.mul( smoothstep( 0, 0.05, progress ) ).mul( smoothstep( 1, 0.95, progress ) )
) )
.build()
} )
onUnmounted( () => lineRef.value?.dispose() )
</script>
<template>
<primitive v-if="lineRef" :object="lineRef" />
</template>Instancing
Render thousands of lines with a single draw call:
vue
<script setup>
import { onMounted, onUnmounted, shallowRef } from 'vue'
import { MeshLine } from 'makio-meshline'
import { Fn, vec3, cos, sin, time, attribute } from 'three/tsl'
const lineRef = shallowRef()
onMounted( () => {
const count = 100
const gpuNode = Fn( ( [progress] ) => {
const offset = attribute( 'instanceOffset', 'vec3' )
const radius = attribute( 'instanceRadius', 'float' )
const angle = progress.mul( Math.PI * 2 ).add( time.negate() )
return vec3( cos( angle ), sin( angle ), 0 ).mul( radius ).add( offset )
} )
const line = new MeshLine()
.instances( count )
.segments( 64 )
.gpuPositionNode( gpuNode )
.lineWidth( 0.05 )
.closed( true )
.build()
line.addInstanceAttribute( 'instanceOffset', 3 )
line.addInstanceAttribute( 'instanceRadius', 1 )
for ( let i = 0; i < count; i++ ) {
const col = i % 10
const row = Math.floor( i / 10 )
line.setInstanceValue( 'instanceOffset', i, [( col - 4.5 ) * 3, ( row - 4.5 ) * 3, 0] )
line.setInstanceValue( 'instanceRadius', i, 0.5 + col * 0.1 )
}
lineRef.value = line
} )
onUnmounted( () => lineRef.value?.dispose() )
</script>
<template>
<primitive v-if="lineRef" :object="lineRef" />
</template>Auto-Resize
Handle window resizing automatically:
js
const line = new MeshLine()
.lines( myPoints )
.lineWidth( 0.1 )
.autoResize( window )
.build()
// autoResize listens for window resize events and updates resolution.
// Cleanup is handled automatically by dispose().Next Steps
- TSL Hooks Guide — all 14 hooks with examples
- Advanced Patterns — GPU positions, instancing, batching
- Performance — optimization tips