Advanced Patterns
These snippets showcase features that go beyond basic styling.
1. GPU-Driven Circle (no vertex upload)
import { MeshLine } from 'makio-meshline'
import { Fn, vec3, cos, sin, time } from 'three/tsl'
const circleNode = Fn(( [progress] ) => {
const angle = progress.mul( Math.PI * 2 ).add( time.negate() )
return vec3( cos(angle), sin(angle), 0 )
})
const segments = 256
const line = new MeshLine()
.segments(segments)
.gpuPositionNode(circleNode)
.closed(true)
.lineWidth(0.3)
.gradientColor(0xffff00)
.build()
scene.add( line )2. Multi-Line Batch
const rings = [ circlePositions(64,1), circlePositions(64,1.5), circlePositions(64,2) ]
const multi = new MeshLine()
.lines(rings)
.closed(true)
.color(0xffffff)
.sizeAttenuation(true)
.lineWidth(2)
.build()One draw-call – three rings.
3. Live Trail (fast setPositions())
// allocate once
const pts = new Float32Array( 64 * 3 )
const trail = new MeshLine()
.lines(pts)
.lineWidth(0.1)
.build()
scene.add( trail )
function update() {
writeTrail( pts ) // mutate coordinates in place
trail.geometry.setPositions( pts )
}No buffers recreated, only a sub-data upload.
4. Sharp Corners & Join Modes
Billboard lines support .join({ type: 'miter' | 'bevel' | 'round', limit, fold: 'geometry' | 'exact' | false }) and .miterLimit( limit ) without changing your point array:
// Crisp bevel at sharp bends (adds 2 bridge triangles per interior corner)
const beveled = new MeshLine()
.lines( myPolyline )
.lineWidth( 0.8 )
.join( { type: 'bevel', limit: 2 } )
.build()
// Exact round silhouette at every bend and projected 3D fold
const rounded = new MeshLine()
.lines( myPolyline )
.lineWidth( 0.8 )
.join( { type: 'round', limit: 2 } )
.build()
// Optional CPU-side corner subdivision (changes logical point count)
const subdivided = new MeshLine()
.lines( myPolyline )
.lineWidth( 0.8 )
.smoothSharpBends( true )
.miterLimit( 2 )
.build()See common-patterns › Sharp Corners and Projected Folds for a side-by-side comparison of the knobs.
5. Animated Dashes
const dashed = new MeshLine()
.lines(sineWavePositions())
.dash({ count: 20, ratio: 0.4 })
.lineWidth(0.4)
.build()
stage.onUpdate.add( dt => {
dashed.material.dashOffset.value += dt * 0.002
})6. Instanced Lines
Render thousands of lines efficiently with instancing:
import { MeshLine } from 'makio-meshline'
import { Fn, attribute } from 'three/tsl'
const instanceCount = 1000
const segments = 32
const transformInstance = Fn( ( [position] ) => {
const offset = attribute( 'instanceOffset', 'vec3' )
const scale = attribute( 'instanceScale', 'float' )
return position.mul( scale ).add( offset )
} )
// Create instanced line
const instancedLine = new MeshLine()
.instances(instanceCount) // Enable instancing
.segments(segments) // Segments per line instance
.lineWidth(0.1)
.color(0xffffff)
.positionFn(transformInstance)
.build()
// Add custom instance attributes
instancedLine.addInstanceAttributes( {
instanceOffset: 3,
instanceScale: 1,
} )
// Fill packed data, then upload each attribute once
const offsets = new Float32Array( instanceCount * 3 )
const scales = new Float32Array( instanceCount )
for ( let i = 0; i < instanceCount; i++ ) {
const offset = i * 3
offsets[offset] = ( Math.random() - 0.5 ) * 20
offsets[offset + 1] = ( Math.random() - 0.5 ) * 20
offsets[offset + 2] = ( Math.random() - 0.5 ) * 20
scales[i] = 0.5 + Math.random() * 1.5
}
instancedLine.setInstanceValues( 'instanceOffset', offsets )
instancedLine.setInstanceValues( 'instanceScale', scales )
scene.add( instancedLine )7. GPU Instanced Circles
Combine GPU position nodes with instancing for animated effects:
import { Fn, vec3, cos, sin, time, attribute, instanceIndex } from 'three/tsl'
const gpuPositionNode = 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 instancedCircles = new MeshLine()
.instances(100)
.segments(64)
.gpuPositionNode(gpuPositionNode)
.lineWidth(0.2)
.colorFn(Fn( ( [color, progress] ) => {
const col = float( instanceIndex ).mod( 10 )
const row = float( instanceIndex ).div( 10 )
return vec3( col.div( 9 ), row.div( 9 ), 0.8 )
} ))
.build()
// Setup instance attributes
instancedCircles.addInstanceAttributes( {
instanceOffset: 3,
instanceRadius: 1,
} )
// Position instances in a grid
for ( let i = 0; i < 100; i++ ) {
const col = i % 10
const row = Math.floor( i / 10 )
const x = ( col - 4.5 ) * 3
const y = ( row - 4.5 ) * 3
instancedCircles.setInstanceValue( 'instanceOffset', i, [x, y, 0] )
instancedCircles.setInstanceValue( 'instanceRadius', i, 0.5 + col * 0.1 )
}8. Compute Curves
MeshLineCompute is an experimental WebGPU-only façade that keeps autonomous curve points in storage buffers and renders them through the normal MeshLine material path.
import { MeshLineCompute } from 'makio-meshline'
const curves = new MeshLineCompute( {
curveCount: 180,
pointsPerCurve: 42,
lineWidth: 0.14,
color: 0x47f7ff,
gradientColor: 0xff4bd8,
transparent: true,
opacity: 0.82,
} ).build()
scene.add( curves )
function animate( delta ) {
curves.step( renderer, delta )
}Use this when point state can stay on the GPU across frames. For small CPU-owned trails, setPositions() remains simpler.
For animated compute curves, use curves.arcLengthNode( progress ) inside material hooks when an effect needs real distance along the current curve. That avoids tying dash spacing or shimmer bands to the normalized point index.
See the MeshLineCompute reference for renderer requirements, simulation controls, and current limits.
9. Material Hooks for Custom Effects
Use material hooks to create custom shader effects:
Dynamic Width Variation
import { Fn, sin, time } from 'three/tsl'
const pulsatingLine = new MeshLine()
.lines(circlePositions( 64 ))
.lineWidth(0.3)
.widthFn(Fn( ( [width, progress] ) => {
return width.mul( sin( time.add( progress.mul( 10 ) ) ).mul( 0.5 ).add( 1 ) )
} ))
.build()Custom Color Gradients
const rainbowLine = new MeshLine()
.lines(sineWavePositions())
.colorFn(Fn( ( [color, progress] ) => {
const hue = progress.mul( 6.28 ).add( time )
return vec3(
sin( hue ).mul( 0.5 ).add( 0.5 ),
sin( hue.add( 2.09 ) ).mul( 0.5 ).add( 0.5 ),
sin( hue.add( 4.18 ) ).mul( 0.5 ).add( 0.5 )
)
} ))
.build()Opacity Fading
const fadingLine = new MeshLine()
.lines(straightLine( 100 ))
.transparent(true)
.opacityFn(Fn( ( [alpha, progress] ) => {
return alpha.mul( smoothstep( 0, 0.1, progress ) ).mul( smoothstep( 1, 0.9, progress ) )
} ))
.build()Custom Dash Patterns
const customDashes = new MeshLine()
.lines(circlePositions( 128 ))
.dash({ count: 10, ratio: 0.3 })
.dashFn(Fn( ( [cyclePos, progress] ) => {
// Create variable dash lengths
const variation = sin( progress.mul( 20 ) ).mul( 0.2 ).add( 1 )
return mod( cyclePos.mul( variation ), float( 1 ) )
} ))
.build()