Separate drifting lenses magnify source details inside circular bubbles. Refraction bends the view near each rim; rim light adds a glass highlight. Unlike Liquid Islands, these bubbles stay separate shapes.
Defaults as a layer
{ "effectId": "bubbleLenses", "params": { "amount": 0.8, "speed": 0.45, "motion": 0.55, "size": 0.85, "coverage": 0.65, "softness": 0.15, "centerX": 0.5, "centerY": 0.5, "magnify": 0.55, "refraction": 0.65, "rim": 0.6 } }
GLSL fragment shader
The effect's own code. The uniforms and helpers above it — uTex, uFeedback, the depth proxy, the frame ring, depthAt(), timeAt() — are the same header every Ether-MOSH shader opens with, and are documented once on the registry rather than reprinted on all 143 pages.
void main(){ vec4 base = texture2D(uTex, vUv); vec2 aspect = vec2(uResolution.x / max(uResolution.y, 1.0), 1.0); vec2 origin = vec2(uCenterX, uCenterY); if(uOrganicShape>0.5) origin=vec2(0.5)+(origin-0.5)*0.35/vec2(max(aspect.x,1.0),max(1.0/aspect.x,1.0)); vec2 p = (vUv - origin) * aspect / max(uSize, 0.01); float t = uTime * uSpeed; float aa = max(1.25 / max(uResolution.y * uSize, 1.0), 0.001) + uSoftness * 0.025; float coverage = 0.0; vec4 art = base; vec2 sampleUv=vUv; float light=0.0; for(int i=0;i<10;i++) { float k=float(i), a=k*2.39996; vec2 center=vec2(cos(a),sin(a))*(0.075+0.027*k); center+=vec2(sin(t*0.6+k),cos(t*0.5+k*2.0))*0.04*uMotion; float radius=0.038+0.021*(0.5+0.5*sin(k*7.3)); vec2 q=(p-center)/radius; float d=length(q); float outline=uOrganicShape>0.5?organicFragment(p-center,radius,k*4.1):length(p-center)-radius; float m=(1.0-smoothstep(-aa,aa,outline))*(1.0-smoothstep(uCoverage*10.0,uCoverage*10.0+1.0,k)); if(m>coverage) { float lens=1.0/(1.0+uMagnify*3.0)+pow(min(d,1.0),3.0)*uRefraction*0.8; sampleUv=clamp(origin+(center+q*radius*lens)*uSize/aspect,0.0,1.0); light=pow(clamp(d,0.0,1.0),10.0)*0.35; light+=exp(-dot(q-vec2(-0.32,0.4),q-vec2(-0.32,0.4))*45.0)*0.6; } coverage=max(coverage,m); } art=texture2D(uTex,sampleUv); art.rgb=mix(art.rgb,vec3(0.85,0.95,1.0),clamp(light*uRim,0.0,1.0)); gl_FragColor = floatingResult(base, art, coverage * smoothstep(0.0, 0.06, uCoverage)); }