Back to Components
Cinematic WebGL Shader Hero Section with Glassmorphism UI
Component

Cinematic WebGL Shader Hero Section with Glassmorphism UI

CodewithLord
October 16, 2025

This project showcases a high-end WebGL-powered hero section featuring a ray-marched prism shader background, cinematic lighting, mouse interaction, and modern glassmorphism UI buttons — all built using pure HTML, CSS, JavaScript, and GLSL shaders.

🧠 Description

This project demonstrates a next-generation hero section design powered by WebGL shaders and advanced UI effects.
The background is a fully procedural ray-marched prism object rendered in real time using GLSL fragment shaders, featuring refraction, fresnel lighting, glow, and nebula-style space visuals.

On top of the shader canvas sits a glassmorphism-based UI layer, including animated gradient borders, shimmer effects, glowing typography, and interactive buttons.

Mouse movement subtly affects both the 3D shader camera and the UI lighting, creating a deeply immersive, cinematic experience — ideal for landing pages, creative portfolios, or premium product showcases.


💻 HTML Structure


1<!DOCTYPE html> 2<html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <title>WebGL Shader Hero Design</title> 7 8 9 </head> 10 11 <body> 12 <style> 13 * { 14 margin: 0; 15 padding: 0; 16 box-sizing: border-box; 17 } 18 body { 19 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; 20 overflow: hidden; 21 background: #000; 22 } 23 canvas { 24 display: block; 25 width: 100%; 26 height: 100vh; 27 } 28 .content { 29 position: fixed; 30 top: 50%; 31 left: 50%; 32 transform: translate(-50%, -50%); 33 z-index: 10; 34 text-align: center; 35 color: white; 36 pointer-events: none; 37 } 38 h1 { 39 font-size: clamp(4.5rem, 13vw, 11rem); 40 font-weight: 900; 41 margin-bottom: 0.5rem; 42 letter-spacing: -0.06em; 43 background: linear-gradient(135deg, #ffffff 0%, #f0f0f0 50%, #ffffff 100%); 44 -webkit-background-clip: text; 45 -webkit-text-fill-color: transparent; 46 background-clip: text; 47 filter: drop-shadow(0 0 40px rgba(255, 255, 255, 0.4)) drop-shadow(0 0 80px rgba(138, 43, 226, 0.3)); 48 animation: glowPulse 3s ease-in-out infinite alternate; 49 } 50 @keyframes glowPulse { 51 from { 52 filter: drop-shadow(0 0 40px rgba(255, 255, 255, 0.4)) drop-shadow(0 0 80px rgba(138, 43, 226, 0.3)); 53 } 54 to { 55 filter: drop-shadow(0 0 60px rgba(255, 255, 255, 0.6)) drop-shadow(0 0 120px rgba(0, 191, 255, 0.4)); 56 } 57 } 58 .tagline { 59 font-size: clamp(0.9rem, 2vw, 1.2rem); 60 font-weight: 300; 61 color: rgba(255, 255, 255, 0.9); 62 letter-spacing: 0.3em; 63 text-transform: uppercase; 64 text-shadow: 0 0 30px rgba(255, 255, 255, 0.5), 0 0 60px rgba(138, 43, 226, 0.3); 65 } 66 .buttons { 67 display: flex; 68 justify-content: center; 69 gap: 24px; 70 margin-top: 40px; 71 pointer-events: auto; 72 } 73 .glass-button { 74 position: relative; 75 padding: 16px 40px; 76 font-size: 1rem; 77 font-weight: 600; 78 letter-spacing: 0.08em; 79 color: #fff; 80 background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.02) 100%); 81 border: 1.5px solid transparent; 82 border-radius: 40px; 83 backdrop-filter: blur(30px); 84 box-shadow: 85 0 8px 32px rgba(0, 0, 0, 0.2), 86 inset 0 1px 0 rgba(255, 255, 255, 0.2), 87 inset 0 -1px 0 rgba(255, 255, 255, 0.05); 88 overflow: hidden; 89 cursor: pointer; 90 transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); 91 pointer-events: auto; 92 } 93 94 .glass-button::before { 95 content: ''; 96 position: absolute; 97 inset: 0; 98 border-radius: 40px; 99 padding: 1.5px; 100 background: linear-gradient(135deg, 101 rgba(255, 255, 255, 0.4) 0%, 102 rgba(138, 43, 226, 0.4) 25%, 103 rgba(0, 191, 255, 0.4) 50%, 104 rgba(255, 105, 180, 0.4) 75%, 105 rgba(255, 255, 255, 0.4) 100%); 106 background-size: 200% 200%; 107 -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); 108 -webkit-mask-composite: xor; 109 mask-composite: exclude; 110 animation: borderFlow 3s linear infinite; 111 opacity: 0.6; 112 transition: opacity 0.5s ease; 113 } 114 @keyframes borderFlow { 115 0% { background-position: 0% 50%; } 116 100% { background-position: 200% 50%; } 117 } 118 .glass-button::after { 119 content: ''; 120 position: absolute; 121 inset: 0; 122 border-radius: 40px; 123 background: radial-gradient(circle at var(--x, 50%) var(--y, 50%), 124 rgba(255, 255, 255, 0.2) 0%, 125 transparent 50%); 126 opacity: 0; 127 transition: opacity 0.5s ease; 128 } 129 .glass-button:hover { 130 background: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.05) 100%); 131 box-shadow: 132 0 12px 48px rgba(138, 43, 226, 0.3), 133 0 0 80px rgba(0, 191, 255, 0.2), 134 inset 0 1px 0 rgba(255, 255, 255, 0.3), 135 inset 0 -1px 0 rgba(255, 255, 255, 0.1); 136 transform: translateY(-3px) scale(1.02); 137 } 138 .glass-button:hover::before { 139 opacity: 1; 140 animation-duration: 2s; 141 } 142 .glass-button:hover::after { 143 opacity: 1; 144 } 145 .glass-button:active { 146 transform: translateY(-1px) scale(0.98); 147 box-shadow: 148 0 6px 24px rgba(138, 43, 226, 0.2), 149 inset 0 1px 0 rgba(255, 255, 255, 0.3); 150 } 151 .glass-button .shimmer { 152 position: absolute; 153 top: -50%; 154 left: -50%; 155 width: 200%; 156 height: 200%; 157 background: linear-gradient( 158 90deg, 159 transparent 0%, 160 rgba(255, 255, 255, 0.1) 45%, 161 rgba(255, 255, 255, 0.3) 50%, 162 rgba(255, 255, 255, 0.1) 55%, 163 transparent 100% 164 ); 165 transform: rotate(30deg); 166 animation: shimmer 3s infinite; 167 pointer-events: none; 168 } 169 @keyframes shimmer { 170 0% { transform: translateX(-100%) rotate(30deg); } 171 100% { transform: translateX(100%) rotate(30deg); } 172 } 173 .glass-button:hover .shimmer { 174 animation-duration: 1.5s; 175 } 176 .glass-button span { 177 position: relative; 178 z-index: 1; 179 } 180</style> 181<canvas id="canvas"></canvas> 182<div class="content"> 183 <h1>PRISM</h1> 184 <p class="tagline">Spectrum of Light</p> 185 <div class="buttons"> 186 <button class="glass-button"> 187 <span class="shimmer"></span> 188 <span>Discover</span> 189 </button> 190 <button class="glass-button"> 191 <span class="shimmer"></span> 192 <span>Join Now</span> 193 </button> 194 </div> 195</div> 196<script id="vertexShader" type="x-shader/x-vertex"> 197 attribute vec2 position; 198 199 void main() { 200 gl_Position = vec4(position, 0.0, 1.0); 201 } 202</script> 203<script id="fragmentShader" type="x-shader/x-fragment"> 204 precision highp float; 205 206 uniform float uTime; 207 uniform vec2 uResolution; 208 uniform vec2 uMouse; 209 210 #define PI 3.14159265359 211 #define TAU 6.28318530718 212 #define MAX_STEPS 80 213 #define MAX_DIST 50.0 214 #define SURF_DIST 0.001 215 216 float hash(float n) { 217 return fract(sin(n) * 43758.5453123); 218 } 219 220 mat2 rot(float a) { 221 float s = sin(a); 222 float c = cos(a); 223 return mat2(c, -s, s, c); 224 } 225 226 float sdSphere(vec3 p, float r) { 227 return length(p) - r; 228 } 229 float sdBox(vec3 p, vec3 b) { 230 vec3 q = abs(p) - b; 231 return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); 232 } 233 234 float sdOctahedron(vec3 p, float s) { 235 p = abs(p); 236 float m = p.x + p.y + p.z - s; 237 vec3 q; 238 if(3.0 * p.x < m) q = p.xyz; 239 else if(3.0 * p.y < m) q = p.yzx; 240 else if(3.0 * p.z < m) q = p.zxy; 241 else return m * 0.57735027; 242 243 float k = clamp(0.5 * (q.z - q.y + s), 0.0, s); 244 return length(vec3(q.x, q.y - s + k, q.z - k)); 245 } 246 float sdTriPrism(vec3 p, vec2 h) { 247 vec3 q = abs(p); 248 return max(q.z - h.y, max(q.x * 0.866025 + p.y * 0.5, -p.y) - h.x * 0.5); 249 } 250 251 float smin(float a, float b, float k) { 252 float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); 253 return mix(b, a, h) - k * h * (1.0 - h); 254 } 255 float smax(float a, float b, float k) { 256 return -smin(-a, -b, k); 257 } 258 259 float map(vec3 p) { 260 vec3 op = p; 261 262 vec2 m = (uMouse - 0.5) * 2.5; 263 p.xy += m * 0.4; 264 265 p.xz *= rot(uTime * 0.12); 266 p.xy *= rot(uTime * 0.08); 267 268 float d = 100.0; 269 270 vec3 p1 = p; 271 p1.yz *= rot(uTime * 0.15); 272 273 float core_distort = sin(p1.x * 3.0 + uTime) * sin(p1.y * 3.0 + uTime) * sin(p1.z * 3.0 + uTime) * 0.1; 274 float core = sdOctahedron(p1, 1.6) + core_distort; 275 276 vec3 p2 = p1; 277 p2.xy *= rot(PI * 0.25 + uTime * 0.2); 278 float prism = sdTriPrism(p2, vec2(1.4, 2.0)); 279 core = smax(core, -prism, 0.2); 280 281 d = core; 282 283 float k_blend = 0.2 + 0.15 * (0.5 + 0.5 * sin(uTime * 1.5)); 284 285 for(int i = 0; i < 4; i++) { 286 float fi = float(i); 287 float angle = fi * TAU / 4.0 + uTime * 0.3; 288 289 float radius = 3.0 + 0.3 * sin(uTime * 0.4 + fi); 290 291 vec3 pos = vec3( 292 cos(angle) * radius, 293 sin(angle * 0.7) * 1.0, 294 sin(angle) * radius 295 ); 296 297 vec3 po = p - pos; 298 po.xy *= rot(uTime * 0.5 + fi); 299 300 float sat_distort = sin(po.x * 5.0 + fi) * sin(po.y * 5.0 + fi) * sin(po.z * 5.0 + fi) * 0.05; 301 float satellite = sdOctahedron(po, 0.4) + sat_distort; 302 303 d = smin(d, satellite, k_blend); 304 } 305 306 return d; 307 } 308 309 vec3 getNormal(vec3 p) { 310 vec2 e = vec2(0.001, 0.0); 311 return normalize(vec3( 312 map(p + e.xyy) - map(p - e.xyy), 313 map(p + e.yxy) - map(p - e.yxy), 314 map(p + e.yyx) - map(p - e.yyx) 315 )); 316 } 317 318 float raymarch(vec3 ro, vec3 rd) { 319 float t = 0.0; 320 for(int i = 0; i < MAX_STEPS; i++) { 321 vec3 p = ro + rd * t; 322 float d = map(p); 323 if(abs(d) < SURF_DIST || t > MAX_DIST) break; 324 t += d * 0.7; 325 } 326 return t; 327 } 328 329 vec3 getBackground(vec3 rd) { 330 float stars = 0.0; 331 vec3 p = rd * 100.0; 332 float h = hash(dot(p, vec3(12.9898, 78.233, 54.53))); 333 if(h > 0.98) stars = pow(h - 0.98, 10.0) * 20.0; 334 vec3 nebula = vec3(0.0); 335 nebula += vec3(0.3, 0.15, 0.5) * pow(max(0.0, sin(rd.x * 2.0 + uTime * 0.1)), 3.0) * 0.2; 336 nebula += vec3(0.15, 0.3, 0.6) * pow(max(0.0, sin(rd.y * 2.5 + uTime * 0.05)), 3.0) * 0.2; 337 338 return stars + nebula; 339 } 340 341 void main() { 342 vec2 uv = (gl_FragCoord.xy - 0.5 * uResolution) / min(uResolution.x, uResolution.y); 343 344 vec2 m = (uMouse - 0.5) * 0.5; 345 vec3 ro = vec3(m.x * 2.0, m.y * 2.0, 5.5); 346 vec3 rd = normalize(vec3(uv, -1.0)); 347 348 rd.xy *= rot(m.x * 0.2); 349 rd.yz *= rot(m.y * 0.2); 350 351 float t = raymarch(ro, rd); 352 353 vec3 color = vec3(0.0); 354 355 if(t < MAX_DIST) { 356 vec3 p = ro + rd * t; 357 vec3 normal = getNormal(p); 358 359 vec3 viewDir = normalize(ro - p); 360 361 float fresnel = pow(1.0 - max(dot(viewDir, normal), 0.0), 3.0); 362 363 float ior = 1.5; 364 vec3 refractDir = refract(rd, normal, 1.0 / ior); 365 366 if(length(refractDir) > 0.0) { 367 float t2 = raymarch(p - normal * 0.01, refractDir); 368 369 if(t2 < MAX_DIST) { 370 vec3 p2 = p - normal * 0.01 + refractDir * t2; 371 vec3 normal2 = getNormal(p2); 372 373 vec3 r = refract(refractDir, -normal2, ior - 0.2); 374 vec3 g = refract(refractDir, -normal2, ior); 375 vec3 b = refract(refractDir, -normal2, ior + 0.2); 376 377 vec3 bgR = getBackground(r) * vec3(1.4, 0.7, 0.7); 378 vec3 bgG = getBackground(g) * vec3(0.7, 1.4, 0.8); 379 vec3 bgB = getBackground(b) * vec3(0.7, 0.8, 1.4); 380 381 color = vec3(bgR.x, bgG.y, bgB.z); 382 color = pow(color, vec3(0.7)) * 5.0; 383 384 } else { 385 color = getBackground(refractDir) * 2.0; 386 } 387 } 388 389 vec3 lightDir = normalize(vec3(1.0, 1.0, -1.0)); 390 vec3 halfDir = normalize(lightDir + viewDir); 391 float spec = pow(max(dot(normal, halfDir), 0.0), 150.0); 392 color += spec * vec3(1.0, 1.0, 1.0) * 3.5; 393 394 vec3 fresnelColor = vec3( 395 0.5 + 0.5 * sin(fresnel * TAU + uTime), 396 0.5 + 0.5 * sin(fresnel * TAU + uTime + TAU / 3.0), 397 0.5 + 0.5 * sin(fresnel * TAU + uTime + TAU * 2.0 / 3.0) 398 ); 399 color += fresnel * fresnelColor * 1.2; 400 401 float edge = pow(1.0 - abs(dot(viewDir, normal)), 4.0); 402 color += edge * vec3(0.6, 0.7, 1.0) * 0.7; 403 404 float sss = pow(max(dot(-normal, lightDir), 0.0), 2.0); 405 color += sss * vec3(1.0, 0.6, 0.8) * 0.5; 406 407 } else { 408 color = getBackground(rd); 409 } 410 411 float vignette = 1.0 - length(uv) * 0.4; 412 vignette = smoothstep(0.3, 1.0, vignette); 413 color *= vignette; 414 415 color *= vec3(0.96, 0.99, 1.06); 416 417 color = pow(color, vec3(0.88)); 418 color *= 1.12; 419 420 gl_FragColor = vec4(color, 1.0); 421 } 422</script> 423<script> 424 const canvas = document.getElementById('canvas'); 425 const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 426 if (!gl) { 427 alert('WebGL not supported'); 428 } 429 430 function resizeCanvas() { 431 canvas.width = window.innerWidth; 432 canvas.height = window.innerHeight; 433 gl.viewport(0, 0, canvas.width, canvas.height); 434 } 435 resizeCanvas(); 436 window.addEventListener('resize', resizeCanvas); 437 438 function createShader(gl, type, source) { 439 const shader = gl.createShader(type); 440 gl.shaderSource(shader, source); 441 gl.compileShader(shader); 442 443 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 444 console.error('Shader compile error:', gl.getShaderInfoLog(shader)); 445 gl.deleteShader(shader); 446 return null; 447 } 448 return shader; 449 } 450 451 function createProgram(gl, vertexShader, fragmentShader) { 452 const program = gl.createProgram(); 453 gl.attachShader(program, vertexShader); 454 gl.attachShader(program, fragmentShader); 455 gl.linkProgram(program); 456 457 if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { 458 console.error('Program link error:', gl.getProgramInfoLog(program)); 459 gl.deleteProgram(program); 460 return null; 461 } 462 return program; 463 } 464 465 const vertexShaderSource = document.getElementById('vertexShader').textContent; 466 const fragmentShaderSource = document.getElementById('fragmentShader').textContent; 467 468 const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource); 469 const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource); 470 const program = createProgram(gl, vertexShader, fragmentShader); 471 472 const uTime = gl.getUniformLocation(program, 'uTime'); 473 const uResolution = gl.getUniformLocation(program, 'uResolution'); 474 const uMouse = gl.getUniformLocation(program, 'uMouse'); 475 476 const positions = new Float32Array([ 477 -1, -1, 478 1, -1, 479 -1, 1, 480 1, 1, 481 ]); 482 const positionBuffer = gl.createBuffer(); 483 gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 484 gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); 485 486 const mouse = { x: 0.5, y: 0.5, targetX: 0.5, targetY: 0.5 }; 487 canvas.addEventListener('mousemove', (e) => { 488 mouse.targetX = e.clientX / canvas.width; 489 mouse.targetY = 1.0 - e.clientY / canvas.height; 490 }); 491 document.querySelectorAll('.glass-button').forEach(button => { 492 button.addEventListener('mousemove', (e) => { 493 const rect = button.getBoundingClientRect(); 494 const x = ((e.clientX - rect.left) / rect.width) * 100; 495 const y = ((e.clientY - rect.top) / rect.height) * 100; 496 button.style.setProperty('--x', x + '%'); 497 button.style.setProperty('--y', y + '%'); 498 }); 499 }); 500 501 let startTime = Date.now(); 502 function render() { 503 const currentTime = (Date.now() - startTime) * 0.001; 504 505 mouse.x += (mouse.targetX - mouse.x) * 0.05; 506 mouse.y += (mouse.targetY - mouse.y) * 0.05; 507 508 gl.clearColor(0.0, 0.0, 0.0, 1.0); 509 gl.clear(gl.COLOR_BUFFER_BIT); 510 gl.useProgram(program); 511 512 gl.uniform1f(uTime, currentTime); 513 gl.uniform2f(uResolution, canvas.width, canvas.height); 514 gl.uniform2f(uMouse, mouse.x, mouse.y); 515 516 const positionLocation = gl.getAttribLocation(program, 'position'); 517 gl.enableVertexAttribArray(positionLocation); 518 gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 519 gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); 520 521 gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); 522 523 requestAnimationFrame(render); 524 } 525 render(); 526</script> 527 528 </body> 529 530</html>


\

HTML Explanation:


The HTML layout is intentionally minimal:

A full-screen renders the WebGL shader background

A fixed .content layer overlays the canvas

UI elements remain independent of the GPU-rendered scene

Buttons are fully interactive despite the canvas underneath


🎨 CSS (Glassmorphism + Typography)


The CSS delivers:

Glassmorphism via backdrop blur

Animated gradient borders

Glow + shimmer effects

Responsive typography with cinematic feel

GPU-friendly animations


Love this component?

Explore more components and build amazing UIs.

View All Components