# Help Porting ShaderToy to Threejs

**URL:** <https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441>\
**Category:** Questions\
**Tags:** shadertoy, fragment-shader, shaders\
**Created:** [April 14, 2018, 12:14pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441 "2018-04-14T12:14:02Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![milewski](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/milewski/32/1521_2.png) [@milewski](https://discourse.threejs.org/u/milewski)\
**Post date:** [April 14, 2018, 12:14pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/1 "2018-04-14T12:14:03Z")

</div>

Hello Guys.

i am trying to port the following shadertoy shader to Threejs: [https://www.shadertoy.com/view/4sG3WV](https://www.shadertoy.com/view/4sG3WV) but i am failing miserably…

the problem with it is that im unsure the sequence that shadertoy renders the buffers (im assuming A, B, Image) and also rendering each Buffer separately i get completely different results from i believe it has something to do with the `vec2 uv = fragCoord.xy / iResolution.xy;` as im translating it to: `vec2 uv = vUv`

this is a sample of the code:

```auto
import { Camera, LinearFilter, Mesh, OrthographicCamera, PlaneBufferGeometry, RGBAFormat, Scene, TextureLoader, Vector3, Vector4, WebGLRenderer, WebGLRenderTarget } from 'three'
import { BufferAShader } from './shaders/BufferAShader'
import { BufferBShader } from './shaders/BufferBShader'
import { BufferImageShader } from './shaders/BufferImageShader'

class App {

    private width = window.innerWidth
    private height = window.innerHeight

    private renderer = new WebGLRenderer()
    private loader = new TextureLoader()
    private mousePosition = new Vector4()
    private orthoCamera = new OrthographicCamera(0, this.width, 0, -this.height, 0, 10000)
    private sceneA: Scene
    private sceneB: Scene
    private sceneC: Scene
    private counter = 0

    constructor() {

        this.renderer.setSize(this.width, this.height)
        document.body.appendChild(this.renderer.domElement)

        this.renderer.domElement.addEventListener('mousedown', () => {
            this.mousePosition.setZ(1)
            this.counter = 0
        })

        this.renderer.domElement.addEventListener('mouseup', () => {
            this.mousePosition.setZ(0)
        })

        this.renderer.domElement.addEventListener('mousemove', event => {

            const x = (event.clientX / this.width) * 2 - 1
            const y = -(event.clientY / this.height) * 2 + 1

            this.mousePosition.setX(x)
            this.mousePosition.setY(y)

        })

    }

    private targetA = new BufferManager(this.renderer, { width: this.width, height: this.height })
    private targetB = new BufferManager(this.renderer, { width: this.width, height: this.height })
    private targetC = new BufferManager(this.renderer, { width: this.width, height: this.height })

    private bufferA: BufferAShader
    private bufferB: BufferBShader
    private bufferImage: BufferImageShader

    public start() {

        const resolution = new Vector3(this.width, this.height, window.devicePixelRatio)
        const channel0 = this.loader.load(require('./images/wallpaper.jpg'))

        /**
         * Target 1
         */
        this.bufferA = new BufferAShader({
            iFrame: { value: 0 },
            iResolution: { value: resolution },
            iMouse: { value: this.mousePosition },
            iChannel0: { value: this.targetA.readBuffer.texture },
            iChannel1: { value: this.targetB.readBuffer.texture }
        })

        this.sceneA = new Scene()
        const meshA = new Mesh(new PlaneBufferGeometry(this.width, this.height), this.bufferA.material)
        this.sceneA.add(meshA)

        /**
         * Target 2
         */
        this.bufferB = new BufferBShader({
            iFrame: { value: 0 },
            iResolution: { value: resolution },
            iMouse: { value: this.mousePosition },
            iChannel0: { value: this.targetB.readBuffer.texture }
        })

        this.sceneB = new Scene()
        const meshB = new Mesh(new PlaneBufferGeometry(this.width, this.height), this.bufferB.material)
        this.sceneB.add(meshB)

        this.bufferImage = new BufferImageShader({
            iResolution: { value: resolution },
            iMouse: { value: this.mousePosition },
            iChannel0: { value: channel0 },
            iChannel1: { value: null }
        })

        this.sceneC = new Scene()
        const meshC = new Mesh(new PlaneBufferGeometry(this.width, this.height), this.bufferImage.material)
        this.sceneC.add(meshC)

        meshA.frustumCulled = false
        meshB.frustumCulled = false
        meshC.frustumCulled = false
        meshA.position.set(this.width / 2, -this.height / 2, 0)
        meshB.position.set(this.width / 2, -this.height / 2, 0)
        meshC.position.set(this.width / 2, -this.height / 2, 0)

        this.animate()

    }

    private animate() {
        requestAnimationFrame(() => {

            this.bufferA.uniforms['iFrame'].value = this.counter++

            this.bufferA.uniforms['iChannel0'].value = this.targetA.readBuffer.texture
            this.bufferA.uniforms['iChannel1'].value = this.targetB.readBuffer.texture
            this.targetA.render(this.sceneA, this.orthoCamera)

            this.bufferB.uniforms['iChannel0'].value = this.targetA.readBuffer.texture
            this.targetB.render(this.sceneB, this.orthoCamera)

            this.bufferImage.uniforms['iChannel1'].value = this.targetA.readBuffer.texture
            this.targetC.render(this.sceneC, this.orthoCamera, true)

            this.animate()

        })

    }

}

class BufferManager {

    public readBuffer: WebGLRenderTarget
    public writeBuffer: WebGLRenderTarget

    constructor(private renderer: WebGLRenderer, { width, height }) {

        this.readBuffer = new WebGLRenderTarget(width, height, {
            minFilter: LinearFilter,
            magFilter: LinearFilter,
            format: RGBAFormat,
            stencilBuffer: false
        })

        this.writeBuffer = this.readBuffer.clone()

    }

    public swap() {
        const temp = this.readBuffer
        this.readBuffer = this.writeBuffer
        this.writeBuffer = temp
    }

    public render(scene: Scene, camera: Camera, toScreen: boolean = false) {
        if (toScreen) {
            this.renderer.render(scene, camera)
        } else {
            this.renderer.render(scene, camera, this.writeBuffer, true)
        }
        this.swap()
    }

}

(new App()).start()

```

the fragment shader is pretty much a control c + control +v from shadertoy and replacing the relevant parts like `gl_FragColor, gl_FragCoord`… `main()` etc… and im using this for the vertexshader

```auto
varying vec2 vUv;

void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}

```

I have uploaded the whole source code here: [https://we.tl/JecAgsGlPA](https://we.tl/JecAgsGlPA)

(as i am a new member i am not able to upload attachment so i hosted it to on weTransfer)

---

<div class="post-metadata">

**Author:** ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)\
**Post date:** [April 14, 2018, 1:28pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/2 "2018-04-14T13:28:53Z")

</div>

> [@milewski](#):
>
> i believe it has something to do with the vec2 uv = fragCoord.xy / iResolution.xy;

That’s the way shader toy computes uv coordinates in the vertex shader. `three.js` uses the uv data of the underlying plane geometry instead. The result is effectively the same. You can verify this if you visualize the uv coordinates in `three.js` like so:

```javascript
gl_FragColor = vec4( vUv.x, vUv.y, 0.0, 1.0 );

```

And in shadertoy via:

```js
fragColor = vec4( uv.x, uv.y, 0.0, 1.0 );

```

You should see the following in both cases:

 ![image](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/2X/1/13fd636d78394273b55158ee63151e6f140f6773.png)

---

<div class="post-metadata">

**Author:** ![milewski](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/milewski/32/1521_2.png) [@milewski](https://discourse.threejs.org/u/milewski)\
**Post date:** [April 14, 2018, 2:56pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/3 "2018-04-14T14:56:09Z")

</div>

Yes i see that gradient of colors, to exemplify better this is what i am seeing against what i should be seeing:

![image](https://cdck-file-uploads-canada1.s3.dualstack.ca-central-1.amazonaws.com/flex035/uploads/threejs/original/2X/e/ed1450088770aadfcaf5539ceb6981c9dbc47ef7.jpg)  
 ![image](https://cdck-file-uploads-canada1.s3.dualstack.ca-central-1.amazonaws.com/flex035/uploads/threejs/original/2X/d/d69f2ef358951d2ba17aa102c2ab13bc027760d2.jpg)

final-buffer.frag

```auto
uniform sampler2D iChannel0; // this is an image
uniform sampler2D iChannel1; // this is the output of buffer-a.frag
varying vec2 vUv;

void main() {
    vec2 uv = vUv;
    vec2 a = texture2D(iChannel1,uv).xy;
    gl_FragColor = vec4(texture2D(iChannel0,a).rgb,1.0);
}

```

buffer-a.frag

```auto
varying vec2 vUv;

void main(){
    gl_FragColor = vec4(vUv,0.0,1.0);
}

```

the same code works on shadertoy: [https://www.shadertoy.com/view/MdGcWd](https://www.shadertoy.com/view/MdGcWd)

---

<div class="post-metadata">

**Author:** ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)\
**Post date:** [April 14, 2018, 3:16pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/4 "2018-04-14T15:16:55Z")

</div>

Any chances you provide a live demo?

---

<div class="post-metadata">

**Author:** ![milewski](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/milewski/32/1521_2.png) [@milewski](https://discourse.threejs.org/u/milewski)\
**Post date:** [April 14, 2018, 4:18pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/5 "2018-04-14T16:18:19Z")

</div>

here you are: [https://codepen.io/RafaelMilewski/pen/NYZpMr](https://codepen.io/RafaelMilewski/pen/NYZpMr)

---

<div class="post-metadata">

**Author:** ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)\
**Post date:** [April 14, 2018, 5:28pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/6 "2018-04-14T17:28:34Z")

</div>

In this demo UV coordinates are float values in the range of [0,1]. If you render these values into a 32bit RGBA buffer (a render target in format `RGBA` and type `UnsignedByte`), you will lose precision since you can only store 8 bit (256 possible integer values) per color channel. This lost is visible if you use the sampled uv coordinates for a texture fetch.

You can fix the issue if you add this parameter when creating the render target `type: THREE.FloatType`. The underlying texture is now a float texture that can hold your uv coordinates and retain precision.

https://codepen.io/anon/embed/preview/bvPWXK?height=300&slug-hash=bvPWXK&default-tabs=js,result&host=https://codepen.io

---

<div class="post-metadata">

**Author:** ![milewski](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/milewski/32/1521_2.png) [@milewski](https://discourse.threejs.org/u/milewski)\
**Post date:** [April 15, 2018, 3:08pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/7 "2018-04-15T15:08:10Z")

</div>

> [@Mugen87](#):
>
> You can fix the issue if you add this parameter when creating the render target type: THREE.FloatType. The underlying texture is now a float texture that can hold your uv coordinates and retain precision.

Well that indeed would take someone with low level knowledge in webgl to figure that out! many thanks! 👍

now my porting is 99% complete, there is only one issue left trough… shadertoy `iMouse` as described in the help section it should be a `vec4` being Z to indicate if it has a click… but it doesnt not specify it is is normalized or what… then the problem is that the Y is in “reverse”…

i have tried the following:

```javascript
// this approach doesnt work at all

this.renderer.domElement.addEventListener('mousemove', event => {

    let x = (event.clientX / this.width) * 2 - 1
    let y = -(event.clientY / this.height) * 2 + 1

    this.mousePosition.setX(x)
    this.mousePosition.setY(y)

})

```

and the following

```javascript
// this approach does work but Y is inverted 
this.renderer.domElement.addEventListener('mousemove', event => {
  this.mousePosition.setX(event.clientX) // x works perfectly 
  this.mousePosition.setY(event.clientY) // y is inverted....
  // this.mousePosition.setY(-event.clientY) // if invert this it doesnt work....
})

```

Here is another code pen to demonstrate:

https://codepen.io/RafaelMilewski/embed/preview/bvPQJy?default-tabs=js%2Cresult&height=300&host=https%3A%2F%2Fcodepen.io&slug-hash=bvPQJy

 ![image](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/2X/b/b758eb35488a0dd8364a138e186d4ebcefabd65a.jpg)

Thanks for the help! 👍

---

<div class="post-metadata">

**Author:** ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)\
**Post date:** [April 15, 2018, 3:29pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/8 "2018-04-15T15:29:25Z")

</div>

That should work:

```auto
this.mousePosition.setX(event.clientX)
this.mousePosition.setY(this.height - event.clientY)

```

---

<div class="post-metadata">

**Author:** ![milewski](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/milewski/32/1521_2.png) [@milewski](https://discourse.threejs.org/u/milewski)\
**Post date:** [April 15, 2018, 3:40pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/9 "2018-04-15T15:40:20Z")

</div>

After overthinking too much ended up not thinking in the less obvious solution 😄 thanks again!

Just to summarize this for anyone looking into porting complex shades from ShaderToy to Three.js:

here is the original shader:

> **[Shadertoy](https://www.shadertoy.com/view/4sG3WV)**

This is the equivalent in threejs:

https://codepen.io/RafaelMilewski/embed/preview/PRrXBo?height=300&slug-hash=PRrXBo&default-tabs=js,result&host=https://codepen.io

Cheers 🙂

---

<div class="post-metadata">

**Author:** ![LouiseLessel](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/louiselessel/32/16121_2.png) [@LouiseLessel](https://discourse.threejs.org/u/LouiseLessel)\
**Post date:** [February 3, 2021, 10:44pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/10 "2021-02-03T22:44:29Z")

</div>

Thank you. This is awesome.

Just in case someone need the full code for iMouse.xyzw it is here:

const iMouse = new THREE.Vector4();  
var iMousePressed = false;

```
	function onMouseMove( event ) {
		// calculate mouse position in normalized device coordinates
		// (-0.5 to +0.5) for both components
		//iMouse.x = ( event.clientX / window.innerWidth ) - 0.5;
		//iMouse.y = (( event.clientY / window.innerHeight ) - 0.5) * -1.;

		// calculate mouse position in normalized device coordinates
		// 0 - 1
		//iMouse.x = ( event.clientX / window.innerWidth );
		//iMouse.y = (( event.clientY / window.innerHeight ) - 1) * -1.;

		// no mapping - pixel coordinates width and height
		iMouse.x = event.clientX ;
		iMouse.y = (event.clientY);
		//console.log(iMouse.x);
		//console.log(iMouse.y);
		//console.log(event.buttons);
		if (event.buttons > 0) {	// button is pressed
			//console.log("pressed");
			if (!iMousePressed) { // if it wasn't pressed last frame
				iMouse.z = iMouse.x; 
				iMouse.w = iMouse.y; 
				iMousePressed = true;
				//console.log(iMouse.z);
				//console.log(iMouse.w);
			}
		}
		if (event.buttons == 0) { 
			iMousePressed = false; 
			//console.log("not pressed");
		}
	}

```

// And remember to add eventlistener in your code  
window.addEventListener( ‘mousemove’, onMouseMove, false );

---

<div class="post-metadata">

**Author:** ![webdot](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/webdot/32/41000_2.png) [@webdot](https://discourse.threejs.org/u/webdot)\
**Post date:** [October 16, 2023, 6:26pm UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/11 "2023-10-16T18:26:36Z")

</div>

Hey @milewski. This example does not display on iOS devices. My guess is because of the multi-rendering process of the buffers.

Is there a way to fix this?

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [October 17, 2023, 2:41am UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/12 "2023-10-17T02:41:35Z")

</div>

which browser?

---

<div class="post-metadata">

**Author:** ![webdot](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/webdot/32/41000_2.png) [@webdot](https://discourse.threejs.org/u/webdot)\
**Post date:** [October 17, 2023, 10:52am UTC](https://discourse.threejs.org/t/help-porting-shadertoy-to-threejs/2441/13 "2023-10-17T10:52:35Z")

</div>

All browsers on iOS. Currently, I have checked Safari, Chrome, and Firefox.  
And they display all the same
