Converting shaderToy to sparksl

I need help converting this shader from shadertoy to sparksl I tried but it didn’t work for me.
paint with displacement.arexport (31.8 KB)

Hi, This effect uses texelfetch
Because spark AR supports a weak version of OpenGL, we cannot directly use the texelfetch code There is only one way to do this

Just calculate the size of a texel by doing 1/resolution and then use that as the uv offset in a normal sparksl texture.sample(uv)

In fact, sometimes you have to rewrite the entire code and this is not an easy task
I rewrote this code and it can be used in spark ar


It’s so hard to see what is going on in those projects when you try to port them, I usually try to reimplement the shader in patches so I can get feedback on what is happening at each step. Here’s a simple UV magnifier painter project. I think it’s mostly the same but it doesn’t account for the pan vector. I’m sure you could add some logic with delay frame if you want to build on top of it.


I appreciate your help but for the past two days I’ve been trying to figure out how to convert “texelFetch” to SparkSL but I couldn’t. So I tried to find similar shaders that didn’t include texelFetch in them but they didn’t work either. I found two more distortion shaders, one compiles successfully but shows a black screen and the other compiles successfully but still shows weird errors when dragging them into the patch editor…
I will be more grateful if you can solve the error of this code. :pray:

Liquify (anastadunbar).arexport (41.4 KB)

Hi and thank you for the time you spend and helping me
i wish there was a tutorial that explained how texelFetch works so novice creators like me could better understand
if this line (vec2 texel = vec2(1./res.x, 1./res.y) converts texelFetch to (uv) then what (ivec2 (U)) does? are they both nested or ivec2 should be converted to something else?
or even if i could converted this, will it work ? cause you said you rewrote the entire code.

I guess the benefit of texelFetch is that it samples discrete pixels and doesn’t blend anything together, so it’s more exact for situations that need pixel precision.

Since we don’t have native texelFetch in spark, we can fake it with a little bit of math. This is all theory at this point - I haven’t actually tried it, but it should work.

Starting from the most basic way to sample textures in SparkSL, you will use the sample method of a std::Texture2d and pass in the UV from std::getVertexTexCoord()


The next step is to make sure this is happening in the fragment shader (which deals in pixels), and not the vertex shader (which can only be as accurate as the number of vertices). So now our method call looks like this:


But even though we are now in fragment / pixel space, your UV coordinates are a continuous value so there’s a chance that it will sample in between pixels and you’ll get an interpolated value. To prevent this, we need to essentially apply a pixelation shader onto our UV coordinates. We can get the pixel dimensions from from vec2 rtSize = std::getRenderTargetSize();, then slice our uv into pixels like this:

vec2 texelUV = floor(fragment(uv) * rtSize) / rtSize;

This method of multiplying, flooring, and dividing will make discrete pixels for us. Then we can finally sample our texels!


I made a github repository for this since it comes up a lot.


  Implementation of texelFetch in SparkSL

export vec4 texelFetch(std::Texture2d tex) {
  vec2 uv = std::getVertexTexCoord();
  vec2 rtSize = std::getRenderTargetSize();
  vec2 texelUV = floor(fragment(uv) * rtSize) / rtSize;
  return tex.sample(texelUV);

vec4 main (std::Texture2d tex){
  return texelFetch(tex);
1 Like

I think you have made mistakes in many areas for shader conversion, for example, you thought iChannelResolution[0].xy is the same as iResolution and you acted in the same way.
While this is how to convert it

vec2 ir0 = BufferA.size;

And texelfetch conversion is a very complex task in general, there is no comprehensive way that you can convert all shaders that use texelfetch code. Sometimes we reach it by multiplying and dividing the coordinates
Sometimes we reach it with the help of textureGrad code and sometimes we define it manually
It depends on the shader code, but any way we do it, all the code changes and it’s like rewriting it.
I think it is better to start with simpler shaders because you have chosen a very advanced topic

1 Like

Yes i tried (BufferA.size) but for some reason it complains about having too many arguments while it already needs 2 arguments
(error: Too many arguments, expected 1 got 2)
and this shader (Liquify (anastadunbar)) has no texelFetch in it.

i really really appreciate your help but i still ended up with black screen :frowning:

1 Like

Even if you can correct this error, you may encounter other errors
Because you used #define
That’s why I say the code should be rewritten from the beginning
Because we work with version 1,2 of openGL, not version 4

You can use regular functions :wink:

1 Like

But i already have tons of converted working shaders that are using #define in them
i think this error occurs because we can’t sample 2 arguments cause when i remove (-100.0) it compiles successfully.

texture.sample(uv, float) :x:
texture.sample(uv) :heavy_check_mark:

i faced the same problem before with other shaders

refer to this to make sure you’re porting the code correctly. some features on shadertoy are not 1:1 to sparksl. Also be careful to read the original code, based on what I see and what I know for quite a while, Mr Fabrice is well known to golfing his code, which is super cool but could be extra confusing for others and especially beginners to read it. I agree with @Dallal it would be much easier to simply just rewrote the code as long as you understand the concept. Also you can follow Josh’s method to workaround the texelfetch, I think that’s a good enough replacement that should work to accomplish what you want to achieve here. Good luck!

1 Like

Yes, I understand where the error is, and I didn’t mean that #define is not supported
I meant that if it exits the #define mode, it will be easier to change the code and get the result
Yes, you can remove -100, but I think you will face a white screen

:+1: ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀