Advertisement

Halloween 2.5D Parallax Shader

| by Vladimir | 3 min read | code by Karim Maaloul
Advanced

Tech & Dependencies

HTML SCSS JavaScript
three.js

Features

  • Custom Shaders
  • Depth Mapping
  • Parallax Effect
  • Mouse Tracking

Browser Support

Chrome 60+ Edge 79+ Firefox 55+ Safari 11+

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 updateSize method 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> and alt text, 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 .world container absolutely.
  • JS: The architecture. The World class 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 custom RawShaderMaterial.

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.

Advertisement