Halloween 2.5D Parallax Shader
See the Pen Halloween 2.5D Parallax Shader.
Tech & Dependencies
Features
- ✓ Custom Shaders
- ✓ Depth Mapping
- ✓ Parallax Effect
- ✓ Mouse Tracking
Browser Support
Core
This is a Halloween 2.5D Parallax Shader. It utilizes Three.js and custom GLSL shaders to transform a flat 2D image into an interactive, volumetric scene with dynamic lighting, fog, and depth distortion. Its function is to provide a deeply immersive, narrative-driven hero section, elevating static artwork into an interactive spatial environment without requiring full 3D models.
Specs
- Weight: ~650 KB (Dependencies: Three.js core).
- Performance: Highly GPU-intensive. The entire visual effect is calculated per-pixel inside the fragment shader sixty times a second.
- Theming / Customization: Hardcoded. Changing the artwork requires four separate, perfectly aligned textures: the base image, a depth map, a normal map, and a noise texture.
- Responsiveness: Fluid. Uses a custom
updateSizemethod to recalculate the Three.js camera projection matrix and aspect ratio based on the window’s dimensions. - Web APIs: WebGL API,
requestAnimationFrame. - Graceful Degradation: [!] Fails completely without JavaScript or WebGL support, displaying a blank black screen. Lacks a standard fallback
<img>andalttext, making it completely invisible to screen readers and search engines.
Anatomy
- HTML: A minimal container (
.world) for the WebGL canvas, and two inline<script>tags housing the raw GLSL code for the vertex and fragment shaders. - CSS: Stripped down. Hides overflow to prevent scrolling and positions the
.worldcontainer absolutely. - JS: The architecture. The
Worldclass sets up the Three.js camera, scene, and renderer. It preloads the four required textures and binds mouse/touch coordinates to a uniform variable, which is fed continuously into the customRawShaderMaterial.
Logic
The architectural core is the fragment shader, which uses multiple texture maps to fake 3D depth and dynamic lighting on a flat plane.
// Calculate displacement based on depth map and mouse position
vec4 texDepth = texture2D(textureDepth, vUv);
float depthVal = texDepth.r - .7;
vec2 dispDepth = vUv + mousePosition * depthVal * .1;
// Dynamic lighting using the normal map
vec3 lightDirection = normalize(vec3(mousePosition.x, mousePosition.y, .3));
vec3 pixDirection = normalize(vec3(texNormal.r * 2.0 - 1.0, texNormal.b * 2.0 - 1.0, -texNormal.g * 2.0 + 1.0));
float lightVal = dot(pixDirection, lightDirection);
texImage.rgb *= .9 + ( distToMouse * lightVal * (1.0 - displacedDepth.g)) * (2.0 + sin(time)*.5);
Instead of moving a 3D camera around 3D objects, the shader manipulates the 2D UV coordinates of the flat image. It reads the greyscale textureDepth map to determine how “close” a pixel is to the camera. When the mouse moves, the shader shifts the UV coordinates of the “closer” pixels more than the “further” pixels, creating a flawless 2.5D parallax illusion. Simultaneously, it reads the textureNormal map to calculate how the surface angles react to a virtual light source tracking the cursor.
Feel
Atmospheric, cinematic, and unsettling. The parallax depth feels remarkably physical, forcing the brain to interpret a flat painting as a tangible diorama. The procedural additions — boiling red fog, erratic lightning flashes, and the spotlight effect trailing the cursor — add layers of analog instability. It transforms a passive viewing experience into an active, exploratory one, where the user feels like they are holding a flashlight in a dark room.


