A step-by-step tutorial walks through building a mouse-following square lens effect using Three.js and GLSL, combining a grayscale image with a color image revealed inside a square mask. The square applies a CC Lens-style distortion and a radial RGB shift that intensify toward its edges, while the grayscale background gets subtle wave and noise-based motion via a random3 GLSL function adapted from a Shadertoy example. The implementation covers project structure, fragment and vertex shader code, aspect-ratio correction for the square mask, mouse position mapping, and adding a lil-gui interface for real-time parameter tweaking, all without post-processing or 3D models.
Table of contents
The IdeaWhat We are BuildingProject StructureSetting Up the StageThe Mesh ClassThe Vertex ShaderThe Fragment ShaderAdding GUI ControlsConclusionQuestions this post answers
How do you create a CC Lens-style distortion effect in a GLSL fragment shader?
A CC Lens distortion is created by centering UV coordinates at the origin, computing the squared radial distance with dot(centeredPosition, centeredPosition), then scaling the position by a distortion-dependent factor: 1.0 + distortion * radius2 for positive distortion, or 1.0 / (1.0 - distortion * radius2) for negative distortion. The difference between distorted and original UVs becomes a sampling offset applied to the texture lookup. See the full shader walkthrough on daily.dev when building similar lens distortion effects with Three.js.
How do you make a square mask maintain its aspect ratio when a WebGL canvas is resized?
Divide the square's UV test coordinates by an aspect-ratio correction factor computed as vec2(min(meshSize.y/meshSize.x, 1.0), min(meshSize.x/meshSize.y, 1.0)). Applying this scale before running the boundary step tests keeps the square's proportions fixed regardless of viewport width or height changes. Developers tuning responsive WebGL shapes can find this kind of shader math on daily.dev.
How do you create a radial RGB shift effect that increases in intensity toward the edges of an area in GLSL?
Compute a direction vector from the area's center to each pixel, ranging from -1.0 to 1.0 per axis, then sample the red, green, and blue channels at offset positions equal to that direction multiplied by separate per-channel shift uniforms (e.g. 0.01 for red, -0.01 for blue). Because the direction is zero at the center, channels overlap there and separate progressively toward the edges. Shader developers refining chromatic aberration effects can track techniques like this on daily.dev.