Makio MeshLine
A modern MeshLine library for Three.js wide screen strokes and world-space road/ribbon surfaces, with gradients, dashes, textures, shadows, GPU positions, and instancing.
Makio MeshLine is built for creative coding, interactive trails, data visualization, and stylized rendering on top of Three.js WebGPURenderer. It gives you a small fluent API for wide and thick lines, while keeping TSL hooks available when you need custom GPU behavior.
[!IMPORTANT]
Version 2 is the current API and package line. Existing V1 applications should review Migrating from v1 to V2 before upgrading because progress, sharp-bend smoothing, validation, surfaces, and optional WebGPU entry points changed.
Features
- TSL-powered — Built with Three.js Shading Language for maximum GPU performance
- WebGPU & WebGL2 — Works with both backends via
WebGPURenderer - Instancing — Render thousands of lines with a single draw call
- 14 GPU hooks — Customize position, color, width, opacity, dash, UV and more in the shader
- Gradients, dashes, textures — Built-in support for common line styles
- Variable width — Per-vertex width control via callback or GPU hook
- Bounded line joins & edge AA —
miter,bevel, androundbillboard joins with camera-invariant 3D fold splitting/clipping (join.fold: 'geometry' | 'exact'), analytic edge antialiasing (.antialias( true )), and optional CPU subdivision - Road and ribbon surfaces — Camera-independent geometry with transport frames, banking, normals, tangents, and distance UVs
- Optional GPU culling — Native-WebGPU chunk compaction and one indirect draw for very large opaque paths
- Shadow support — Cast and receive shadows across billboards (with 3D cylindrical tube normals) and world-space surfaces
- GPU positions — Procedural line generation entirely on the GPU
- Compute-driven trails & morphing —
storagePositions()draws lines straight from WebGPU storage buffers, with ring-buffer trails and two-buffer contour morphing - Tree-shakeable — ESM-only,
sideEffects: false, import only what you need - TypeScript — Full type definitions included
- Zero dependencies — Only Three.js as peer dependency
Why Use Makio MeshLine?
- Replace older
THREE.MeshLinestyle workflows with a WebGPU-ready Three.js line renderer. - Use the same API for one-off wide lines, repeated batches, or thousands of instanced lines.
- Keep CPU uploads low with
setPositions(), localsetPositionRange()edits, bulk instance writes, batching, and GPU position nodes. - Add styling and motion with gradients, dashes, textures, vertex colors, opacity, and TSL hooks instead of maintaining multiple line systems.
Use Cases
- Interactive cursor trails, drawing tools, and paint-like strokes
- Motion design, sci-fi wireframes, stylized ropes, and decorative outlines
- Data visualization, flow fields, and parametric curves
- Large repeated scenes such as vegetation, hair-like structures, or wire sculptures
- Roads, tracks, and textured world-space ribbons
Support Matrix
| Requirement | Version |
|---|---|
| Three.js | r186+ (every entry point) |
| Optional gpu-culling / gpu-path entries | Native WebGPU |
| Renderer | WebGPURenderer only |
| Backends | WebGPU & WebGL2 (via WebGPURenderer) |
The release gate installs the packed library into clean consumers and runs runtime plus declaration smoke tests against the minimum supported version (r186.0) and the pinned workspace version. A separate Three.js dev branch canary is advisory so upstream movement stays visible without making releases depend on a moving target.
Live Demo
See it in action with examples on https://meshline-demo.makio.io and check out the code here : demo/src/demos
Installation
pnpm add makio-meshline # or npm/yarn
Upgrading an existing v1 application? Review the V2 migration guide before changing the dependency. Progress is now distance-based, sharp-bend smoothing is opt-in, invalid configuration fails earlier, and the optional native-WebGPU imports have a narrower Three.js version requirement.
Quick Start
import * as THREE from 'three/webgpu'
import { MeshLine, circlePositions } from 'makio-meshline'
// 1. Basic Three.js scaffolding ------------------------------------------------
const renderer = new THREE.WebGPURenderer()
renderer.setSize(innerWidth, innerHeight)
document.body.appendChild(renderer.domElement)
await renderer.init()
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 100)
camera.position.z = 4
// 2. Create a line (fluent API) ------------------------------------------------
const line = new MeshLine()
.lines(circlePositions(64), true) // 64-segment circle, closed loop
.color(0xff6600) // hex or THREE.Color
.lineWidth(0.4) // scene-space width (sizeAttenuation on by default)
scene.add(line)
// 3. Render loop ---------------------------------------------------------------
renderer.setAnimationLoop(() => renderer.render(scene, camera))
Road / Ribbon Surface
Use the separate surface profile when the width and orientation must live in world space:
const road = new MeshLine()
.lines(roadPoints)
.lineWidth(4)
.surface({
frame: 'transport',
up: [0, 1, 0],
uvMode: 'distance',
uvScale: 5
})
.join({ type: 'round', limit: 3, segments: 8 })
.cap({ type: 'round', segments: 8 })
.build()
The ordinary miter/butt case stays on the compact two-row path. Bevels, round fans, caps, over-limit miters, and reversal splits add topology only where required. See World-space surfaces for banking, PBR maps, join policies, and precision guidance.
Optional GPU Culling
Very large opaque paths can opt into native-WebGPU chunk compaction (native WebGPU only, from an optional entry point):
import MeshLineCullingSystem from 'makio-meshline/gpu-culling'
const culling = MeshLineCullingSystem.attach(renderer, scene, { mode: 'auto' })
const handle = culling.add(line)
// The first draw warms the compact index buffer; then prepare a culling result.
renderer.render(scene, camera)
await culling.update(camera)
// Explicit diagnostic readback of the active result.
const snapshot = await handle.readCullingSnapshot()
console.log(snapshot?.culledChunks)
Unsupported backends, cameras, materials, or bounds draw the complete geometry. See GPU culling for the bounds contract, chunk-range snapshot format, readback cost, and profiling guidance.
Experimental GPU Path
Very large static open paths can instead use the point-centric native-WebGPU renderer:
import MeshLineGPUPath from 'makio-meshline/gpu-path'
const path = new MeshLineGPUPath( { lines: points, lineWidth: 0.35 }, {
chunkSegments: 128,
simplify: { localTolerance: 0.05 },
} )
scene.add( path )
await path.prepare( renderer, camera )
It emits stable segment records and one non-indexed indirect draw while retaining a complete CPU MeshLine fallback. See GPU path for its deliberately narrow local-space guarantee and current unsupported profiles.
Vanilla Three.js Example
The vanilla starter follows the familiar Three.js example structure: init(), onWindowResize(), and animate(), with explicit WebGPU renderer and OrbitControls setup.
pnpm --filter makio-meshline-vanilla-example dev
Framework Quickstart
Use Makio MeshLine with React Three Fiber or Vue. Each example ships a declarative wrapper and is ready to open on StackBlitz.
React Three Fiber
import { Canvas } from '@react-three/fiber'
import { circlePositions } from 'makio-meshline'
import { WebGPURenderer } from 'three/webgpu'
import { MeshLine } from './MeshLine.jsx' // wrapper from the example
const circlePoints = circlePositions( 64, 3 )
async function createRenderer( props ) {
const renderer = new WebGPURenderer( { ...props, antialias: true } )
await renderer.init()
return renderer
}
export default function App() {
return (
<Canvas
camera={{ position: [0, 0, 10] }}
gl={createRenderer}
>
<MeshLine
points={circlePoints}
closed
lineWidth={0.2}
color={0xff8800}
gradientColor={0xffffff}
/>
</Canvas>
)
}
Full example & wrapper source: examples/react-three-fiber/ — docs
Vue
<script setup>
import { circlePositions } from 'makio-meshline'
import MeshLine from './MeshLine.vue'
defineProps( {
parent: { type: Object, required: true },
} )
const points = circlePositions( 64, 3 )
</script>
<template>
<MeshLine
:parent="parent"
:points="points"
closed
:line-width="0.2"
:color="0xff8800"
:gradient-color="0xffffff"
/>
</template>
Pass any Three.js Object3D already attached to your scene as parent.
Full example & wrapper source: examples/vue/ — docs
Documentation
Check out the documentation site for detailed instructions, API reference, and examples.
- Why Makio MeshLine
- Getting started
- API overview
- Common Patterns
- Advanced Patterns
- Performance tips
- V2 Benchmarks
- World-space roads and ribbons
- GPU culling
- Experimental GPU path
- Migrating from v1 to V2
- V2 design contract
- Release notes and checklist
- Helpers functions
- GPU picking & debug helper
- React Three Fiber integration
- Vue integration
About
The concept was to build a performant but easily customizable MeshLine with TSL for WebGPURenderer, allowing the devs to focus on creativity instead of implementation. (read more about it)
Coming from THREE.MeshLine?
If you've been using spite/THREE.MeshLine, makio-meshline is a modern replacement built for the Three.js WebGPU era. The API is different (fluent/chainable), but the concepts are familiar. Start with Getting Started for the Makio MeshLine API. The V2 migration guide is specifically for existing makio-meshline v1 applications.
Thanks
A big thanks to the community, especially to my friend @Floz for his constant support, Samsyyyy for the early tests & feedbacks, to TheSpite for making the first version i knew of MeshLine, to MrDoob and Sunag for Three.js and TSL!
Learn more about meshlines :
Contributing
Bug reports, feature requests and PRs are welcome. Please open an issue first to discuss major changes.
License
This project is licensed under the MIT License .
Made with ❤️ by Makio64