Is there a description of the tsl commands?

Is there a description of how mathematical commands look like in tslFn functions?

When I use Math.sin(x) or sin(x) I get a black screen. Instead of * I have to use mul and instead of + add.
For more complex formulas I imagine this to be less practical.

I want to translate this glsl code into a tslFn code.

ivec2 texSize = textureSize(vertexTexture, 0);
float texWidth = float(texSize.x);
float texHeight = float(texSize.y);
int colIdx = int(floor(idx / texWidth));
int rowIdx = int(mod(idx, texHeight));
vec3 posData = texelFetch(vertexTexture, ivec2(rowIdx, colIdx), 0).rgb;

I understand how to use uniforms and attributes. Now all I need is a description of the three-shader-language

this.customShaderParams = {
   vertexTexture: texture( DataTextureVertices ),
   time: uniform(this.dt),
   position: attribute('position'),

const ShaderNodePosition = tslFn((params) => {

//what is the tsl command for textureSize?
//what is the tsl command for floor?
//what is the tsl command for mod?
//what is the tsl command for texelFetch?


const material = new MeshBasicNodeMaterial();
material.positionNode = ShaderNodePosition(this.customShaderParams);				

Slight off-topic - but is it just me who’s absolutely out of sync, or is three-shader-language something experimental / custom / new / hidden within source? First time I hear a reference to it, nothing in here / docs / source I could find about it :eyes:

1 Like

tsl = three shader language
or am I wrong?

In r155 “func” was replaced by “tslFn” or “wgslFn” depending on whether you want to work in tsl or wgsl language. I decided to get more involved with tsl, because if 3js is already trying so hard to develop an optimized node system with its own language, I would also like to understand it.

Hm… :thinking: but now that I had to think about wgslFn because of you, I could do it with that too, since there is enough documentation on wgsl.

But it would be nice to learn more about tsl

1 Like

There aren’t any docs yet, it’s all in this example:

Basically a way of writing WebGPU shaders with JavaScript instead of WGSL directly.

I have now found out how the node system works and I can recreate everything I have done so far in WebGL in WGSL. I think it’s great. As for TSL, I’m just curious.
I will close this point so that it no longer appears as an open question. The node system is really a very good thing, I like it.
I think if I wanted I could also transfer my WGSL shaders to TSL as long as the corresponding functionality already exists in TSL. But I like wgsl and for very mathematical shaders I find * instead of mul() and + instead of add() and so on more convenient.

Interesting to know. Do the nodes cover every aspect of WGSL? Or do you write custom nodes using WGSL as well? Seems like TSL would be an alternative “syntax” for cases where you might write a new node.

I see how the operators are terser and less verbose. I wonder though, if the following would be of a substantial value that outweighs writing code in plain WGSL (thinking along the lines of glslify and reusable libs, but in this case JS code):

The nodes certainly do not cover the entire range of functions of WGSL. The “textureStore” node came with r156. Sunag kindly developed this when I asked for it and merged it into r156. But now with r157 there will be something else that will improve the work with it, the StorageTexture class. That’s really great. In my opinion, this makes DataTextures obsolete. With the node system as it currently is, I can do everything I did before in glsl.

Here is a wgsl example in r156 from me. But I will adapt that with r157, because the StorageTexture class that comes with r157 will be more practical than the THREE.Texture()

const butterflyWGSL = wgslFn(`  

  fn computeWGSL(
      storageTex: texture_storage_2d<rgba8unorm, write>,
      index: u32,
      resolution: f32,
  ) -> void {

      var posX = index % u32(log2(resolution));
      var posY = index / u32(resolution);
      let indexUV = vec2u( posX, posY );
      let N: f32 = resolution;

      var k: f32 = (f32(posY) * N/pow(2, f32(posX) + 1)) % N;
      var twiddle: vec2<f32> = vec2<f32>(cos(2 * PI * k / N), sin(2 * PI * k / N));
      var butterflyspan: f32 = pow(2, f32(posX));
      var butterflywing: u32 = 0;

      if(f32(posY) % pow(2, f32(posX) + 1 ) < pow(2, f32(posX))){
        butterflywing = 1;
        butterflywing = 0;
      if(u32(posX) == 0){
         if(butterflywing == 1){
            textureStore(storageTex, indexUV, vec4f(twiddle.x, twiddle.y, f32(bitReverse(posY)), f32(bitReverse(posY + 1))));
            textureStore(storageTex, indexUV, vec4f(twiddle.x, twiddle.y, f32(bitReverse(posY - 1)), f32(bitReverse(posY)) ));
         if(butterflywing == 1){
            textureStore(storageTex, indexUV, vec4f(twiddle.x, twiddle.y, f32(posY), f32(posY) + butterflyspan ));
            textureStore(storageTex, indexUV, vec4f(twiddle.x, twiddle.y, f32(posY) - butterflyspan, f32(posY) ));

   const PI: f32 = 3.141592653;

   fn bitReverse(value: u32) -> u32 {
      var numBits: u32 = 32u;
      var result: u32 = 0u;

      for(var i: u32 = 0u; i < numBits; i = i + 1u){
         result = result | (((value >> i) & 1u) << (numBits - 1u - i));  
      return result;

this.butterflyParams = {
	resolution: 512,
	workgroup_size: [1, 16, 1],

this.butterflyTexture = new THREE.Texture();
this.butterflyTexture.image = {width: Math.log2(this.butterflyParams.resolution), height: this.butterflyParams.resolution};
this.butterflyTexture.magFilter = this.butterflyTexture.minFilter = THREE.NearestFilter;

this.butterflyCall = butterflyWGSL({
	storageTex: textureStore(this.butterflyTexture),
	index: instanceIndex,
	resolution: this.butterflyParams.resolution,

this.computeButterfly = this.butterflyCall.compute(this.butterflyParams.resolution * this.butterflyParams.resolution);
this.renderer.compute(this.computeButterfly, this.butterflyParams.workgroup_size);

If you want to change uniforms as usual in glsl, you have to define them as such. In my case here, the “resolution: 512” in the params are a constant that cannot be changed.

Here I have a simple example of where I use the time. I can then use this as a uniform as usual:

The node system is already very advanced. And it is very well thought out. The developers have done a very good job :+1: