A visual node-graph editor for building Three.js Shading Language (TSL) materials with real-time WebGPU preview and glTF export. Built with React 19, TypeScript, Three.js r182, and Vite 7.
- Visual node graph for composing TSL materials
- Real-time WebGPU preview with multiple geometry options
- Multiple material types: Standard, Physical, Basic, Toon, Phong, Matcap, and Normal
- TSL code generation with live code viewer
- Export to TSL code, material code, full app code (JS/TS), and glTF
- Reusable function nodes for sub-graph composition
- Graph persistence via IndexedDB with named save slots
- Undo/redo history
- GLTF geometry, material, and texture import nodes
- KTX2 texture support via Basis Universal transcoder
- 185+ node types including math, noise, oscillators, gradients, fog, color manipulation, and more
- Customizable presets for quick starting points
- Node.js 18+
- Browser with WebGPU support (Chrome 113+ / Edge 113+)
npm install
npm run devThis starts the example app dev server. Open the URL shown in your terminal.
tsl-node-editor/
├── packages/tsl-node-editor/ # Library package (embeddable React component)
│ ├── src/
│ │ ├── TSLNodeEditor.tsx # Main editor component
│ │ ├── TSLNodeEditor.css # Editor styles
│ │ ├── viewer.ts # Standalone WebGPU viewer (iframe, no React)
│ │ ├── tslGltfExporter.ts # TSL node serialization for glTF
│ │ └── index.ts # Public exports
│ ├── public/basis/ # KTX2 Basis Universal transcoder assets
│ ├── vite.config.ts # Vite library mode build config
│ └── package.json
├── examples/
│ ├── basic/ # Minimal example using the library
│ └── material-editor/ # Advanced example with scene + editor overlay
├── package.json # npm workspace root
└── eslint.config.js
npm install tsl-node-editor react react-dom threereact, react-dom, and three are peer dependencies. They stay in the consuming app and are not bundled into the library.
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { TSLNodeEditor } from 'tsl-node-editor'
import 'tsl-node-editor/style.css'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<TSLNodeEditor />
</StrictMode>,
)import { TSLNodeEditor, EXAMPLE_GRAPH_PRESET } from 'tsl-node-editor'
import 'tsl-node-editor/style.css'
<TSLNodeEditor
className="my-editor"
style={{ height: '100vh' }}
viewerUrl="/viewer.html"
basisPath="/basis/"
initialGraph={EXAMPLE_GRAPH_PRESET.graph}
disablePersistence
onChange={({ tslCode, materialCode, appCode, graph }) => {
console.log(tslCode)
}}
/>| Prop | Type | Description |
|---|---|---|
className |
string |
CSS class for the root element |
style |
React.CSSProperties |
Inline styles for the root element |
viewerUrl |
string |
URL for the glTF viewer iframe |
basisPath |
string |
Path to KTX2 Basis transcoder assets (default: "/basis/") |
onChange |
(output: TSLNodeEditorOutput) => void |
Callback fired when the graph changes |
initialGraph |
TSLNodeEditorGraph |
Initial graph state to load |
disablePersistence |
boolean |
Disable IndexedDB save/load |
hideGithubLink |
boolean |
Hide the GitHub link in the UI |
presets |
TSLNodeEditorPreset[] |
Custom preset graphs available in the UI |
| Export | Kind | Description |
|---|---|---|
TSLNodeEditor |
Component | The main editor component |
EXAMPLE_GRAPH_PRESET |
Constant | Built-in example graph (animated torus) |
createDefaultNodeSerializer |
Function | glTF export helper for TSL node serialization |
TSLNodeEditorProps |
Type | Props for the editor component |
TSLNodeEditorOutput |
Type | Shape of the onChange callback argument (tslCode, materialCode, appCode, graph) |
TSLNodeEditorGraph |
Type | Serializable graph state (nodes, connections, groups, functions) |
TSLNodeEditorPreset |
Type | Preset definition (name + graph or URL) |
Consuming apps must serve two additional assets:
viewer.html— A standalone HTML page that loads the library's viewer module. This renders the WebGPU preview in an iframe.public/basis/— Basis Universal transcoder files (basis_transcoder.jsandbasis_transcoder.wasm) for KTX2 texture support.
See examples/basic/ for a complete working setup.
# From root (workspace)
npm run dev # Start example dev server
npm run build # Build library, then examples
npm run lint # ESLint across all packages
npm run preview # Preview example production build
# Per-package
npm run build -w packages/tsl-node-editor # Build library only
npm run build -w examples/basic # Build example only
npm run dev -w examples/basic # Dev server for basic example- This is an experimental project and has not been thoroughly tested.
- Development follows a vibe-coding approach. Pull requests are not accepted.
- Issue reports and feature requests are welcome at GitHub Issues.
MIT