Blender to TSL/Use it
Using the module in three.js
Import the exported material, load its textures, drive its uniforms, and match Blender's colours.
Each exported file is an ES module with one job: build the material. It imports three.js, exports a factory function, and lists what it needs from your project (textures, attributes and settings). You don’t need anything else from the add-on at runtime.
Requirements
WebGPURendererfromthree/webgpu. It switches to WebGL 2 by itself in browsers without WebGPU. The classicWebGLRenderercan’t render node materials.- three.js r186 is what the add-on is tested with. Newer versions usually work; TSL is still changing, so check the console after you upgrade three.
The module imports from 'three/webgpu' and 'three/tsl'. If your project resolves three differently (an import map or an alias), edit those two import lines, or have an AI agent export with your project’s specifiers.
Create the material
import createBrickWallMaterial from './materials/BrickWall.tsl.js';
const { material, uniforms } = createBrickWallMaterial();
mesh.material = material;
The factory is the module’s default export, and is also exported by name: create + the material name + Material. Each call returns a new material, plus the uniforms that drive it.
Textures
A material that uses images exports TEXTURES, keyed by image name. Each entry has the file name and the sampling settings from Blender. Load every file and pass them in:
import { TextureLoader } from 'three/webgpu';
import createBrickWallMaterial, { TEXTURES } from './materials/BrickWall.tsl.js';
const loader = new TextureLoader();
const textures = {};
for (const [key, { file }] of Object.entries(TEXTURES)) {
textures[key] = await loader.loadAsync(`/materials/${file}`);
}
const { material } = createBrickWallMaterial({ textures });
- Export .js with Copy Images on puts the images next to the module. Serve them from wherever your project keeps static files, and change the URL above to match.
- The factory configures each texture for you:
flipY, colour space, wrapping and filtering all come from Blender. Don’t set them yourself. - A texture you don’t pass logs
[tsl] missing texture "<key>"in the console. - Any loader works, including
KTX2Loaderfor compressed textures.
Uniforms
uniforms maps names to TSL uniform nodes. Change .value at runtime, with no rebuild:
uniforms.tint.value.set('#ff6a3d'); // an RGB node labelled "Tint"
uniforms.glowStrength.value = 2.5; // a Value node labelled "Glow Strength"
Value and RGB nodes become uniforms (with Value/RGB as Uniforms on), named after their node labels in camelCase: Glow Strength becomes glowStrength. Label the nodes you want to drive in Blender before you export; unlabelled nodes use the node name. Other uniforms come from things Blender knows about the scene and the object:
| Uniform | Comes from | What to do |
|---|---|---|
frame |
Keyframed values, e.g. an animated Mapping node | Set it every frame: seconds × fps + frame_start, using the .blend’s frame rate and start frame. It starts at the frame that was current at export. |
objectRandom, object colour |
Object Info | Defaults to Blender’s value for this object. Set it per mesh if you reuse the material on several. |
ambientLight, sunDirection, sunColor |
Shader to RGB | Drive them from your scene’s lights (below). |
renderer.setAnimationLoop((time) => {
uniforms.frame.value = (time / 1000) * 24 + 1; // 24 fps, starting at frame 1
renderer.render(scene, camera);
});
Keyframes with linear or constant interpolation play exactly as on Blender’s timeline. Eased keys are linear between keys, and F-curve modifiers are ignored; the export notes say so when it happens.
Shader to RGB lighting
three.js can’t feed its lights into a material’s node graph the way Blender’s Shader to RGB does, so toon-style materials use a simple light from three uniforms:
sunDirection: a world-space unit vector pointing towards the light. For aDirectionalLight, that’slight.position − light.target.position, normalised.sunColor: the light’scolor × intensity / π.ambientLight: your ambient or world colour.
Mesh data
If the material reads attributes or vertex colours, the module exports ATTRIBUTES, mapping Blender’s names to the names it reads on the geometry, e.g. { rand: '_rand' }. Exporting the mesh covers getting those into a glTF. Edit the values if your pipeline names attributes differently.
Transparency and sides
The factory sets transparent, alphaHash and side from the Blender material’s render method and backface settings. Don’t override them.
Matching Blender’s colours
Colours are scene-linear, the same working space three.js uses. To match Blender, set the renderer’s tone mapping to the one that matches your Color Management ▸ View Transform:
| Blender view transform | three.js renderer.toneMapping |
|---|---|
| Standard | NoToneMapping |
| AgX (Blender’s default) | AgXToneMapping |
| Khronos PBR Neutral | NeutralToneMapping |
Other view transforms, such as Filmic, have no three.js equivalent; the export notes tell you. three’s AgX lifts dark, saturated colours a little compared with Blender’s.
What the module says about itself
META (exported by every module) is the export report as plain data: the material class, the requirements (renderer, tone mapping, alpha, double-sided, UV maps, vertex colours, tangents), the uniforms with their roles, the attributes and the conversion notes. It’s handy for checking a module in code or tests:
import { META } from './materials/BrickWall.tsl.js';
console.log(META.requirements.tone_mapping); // e.g. "AgXToneMapping"
The code is yours
The modules the add-on generates, including the helper functions copied into them, are yours to use, change and ship under any terms, including in closed-source sites and apps. No attribution is needed.
One exception: code translated from Blender’s Cycles renderer (its maths, colour blends, some textures, bump and normal maps) is Apache-2.0. A module that contains any says so in a /*! … */ notice at the top. Apache-2.0 allows closed-source use; keep that notice with the code when you ship it. Bundlers keep /*! comments by default.