I can reproduce on an iPad Air 2 running iOS 9.3, but I think the signs are pointing to a mobile Safari or graphics driver bug.
Interesting. I have 3 devices running iOS 9.3 and they all have the issue (iPhone 6s, iPod 6th gen, iPod 5th gen). So it might specific to iOS 9.3.
I also cannot reproduce on Chrome, Firefox, Edge (on Windows 10) or Chrome for Android, or Safari 9 on a Macbook. This suggests the Construct 2 code is correct (since it works across a broad array of browser engines, OSs and graphics hardware) and that it's possibly not a Safari bug (since it works on OS X). It could possibly be a Safari bug that manifests in the mobile variant only, or a graphics driver bug (I think iOS devices same the share similar graphics hardware/drivers, and while iOS is generally good on driver quality, our past experience is all drivers have buggy behavior at some point).
I agree it is an iOS quirk. I don't know if it's software or hardware. My hope is that it's something simple which can be coded around or accounted for in the writing of effects which deal with transparency.
Your simpler example uses a third-party effect, which we cannot accept in bug reports. (There's a built-in replace color effect anyway?) I think your theory about transparency turning black is probably correct, but to investigate further can you provide the simpler demo with no third-party addons?
That was really silly of me. I setup the simpler example with the built-in replace colour effect which suffers from this problem but I started working on trying to find a workaround and I was saving the file into Dropbox as I worked.
I have found a workaround but it required gutting some of the functionality of the built-in effect.
Here is a clean version of the simpler example -- i.e. using just the built-in replace colour effect:
Here is the copy of my simpler colour effect that does the job:
// Replace color effect
varying mediump vec2 vTex;
uniform lowp sampler2D samplerFront;
//uniform mediump sampler2D samplerFront;
uniform mediump float rsource;
uniform mediump float gsource;
uniform mediump float bsource;
uniform mediump float rdest;
uniform mediump float gdest;
uniform mediump float bdest;
uniform lowp float tolerance;
mediump vec4 front = texture2D(samplerFront, vTex);
mediump float alpha = front.a;
if (front.a == 0.0)
gl_FragColor = front;
front.rgb /= front.a;
// Calculate distance from source color
lowp float diff = length(front.rgb - vec3(rsource, gsource, bsource) / 255.0);
front.rgb = mix(front.rgb, vec3(rdest, gdest, bdest) / 255.0, 1.0);
front.rgb *= front.a;
gl_FragColor = front;
The functionality I ended up droping was the concept of a matching threshold -- so this now replaces all colour in a sprite -- although I don't believe it was actually necessary. I think the issue was related to values of zero alpha being used in the rest of the equation, therefore I identified that if I just returned out of the code as soon as I detected an alpha of zero it actually resolved the problem.