Back to Components
Halloween — Valley of Ghouls | Spooky Animated Halloween Website using HTML, CSS & JavaScript
Component

Halloween — Valley of Ghouls | Spooky Animated Halloween Website using HTML, CSS & JavaScript

CodewithLord
October 14, 2025

Experience the spooky charm of Valley of Ghouls, a Halloween-themed animated website built using HTML, CSS, and JavaScript.

🧠 Description

Experience the spooky charm of Valley of Ghouls, a Halloween-themed animated website built using HTML, CSS, and JavaScript. It features glowing neon text, floating ghost animations, and a dynamic background that transforms with eerie lighting effects. Perfect for learning creative front-end design, CSS animations, and event-based interactivity in JavaScript.


💻 HTML Code

1<!DOCTYPE html> 2<html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <title>Halloween - valley of ghouls ( gooey outline )</title> 7 <link rel="stylesheet" href="./style.css"> 8 9 </head> 10 11 <body> 12 <div id="walkerWrapper"> 13 <svg id="walker" style="overflow: visible;" width="100" height="100" viewBox="0 0 100 100" version="1.1" 14 xmlns="http://www.w3.org/2000/svg" 15 xmlns:svg="http://www.w3.org/2000/svg"> 16 17 <g id="weapon01Wrapper" fill="#AAA" fill-opacity="0.75" stroke="none"> 18 <path fill="none" stroke="none" d="M 110,50 A 60,60 0 0 1 50,110 60,60 0 0 1 -10,50 60,60 0 0 1 50,-10 60,60 0 0 1 110,50 L 50,50 Z" /> 19 <path id="weapon01" d="M 50,2 C 39,2 30,4 21,9 c -4,3 -2,6 3,4 6,-3 20,-5 20,-5 0,1 1,2 3,3 L 43,52 H 57 L 53,11 c 2,0 4,-2 4,-3 0,-1 15,2 21,6 5,2 7,-1 2,-4 C 72,5 61,2 50,2 Z" /> 20 </g> 21 22 <g id="weapon02Wrapper" fill="#AAA" fill-opacity="0.75" stroke="none"> 23 <path fill="none" stroke="none" d="M 110,50 A 60,60 0 0 1 50,110 60,60 0 0 1 -10,50 60,60 0 0 1 50,-10 60,60 0 0 1 110,50 L 50,50 Z" /> 24 <path id="weapon02" d="m 17,13 1,2 c 0,1 -6,3 -6,3 0,0 0,5 2,4 l 6,-3 c 1,1 2,1 5,1 L 43,52 H 57 L 30,17 c 1,-1 3,-1 3,-2 0,-1 -1,-2 -1,-2 21,-6 37,-3 60,16 5,4 1,-2 1,-2 C 85,10 69,-3 35,7 32,8 30,9 30,9 L 28,6 C 27,5 16,11 17,13 Z" /> 25 </g> 26 27 <g id="wMain" fill="#AAA" fill-opacity="0.75" stroke="none"> 28 <path fill="none" stroke="none" d="M 110,50 A 60,60 0 0 1 50,110 60,60 0 0 1 -10,50 60,60 0 0 1 50,-10 60,60 0 0 1 110,50 L 50,50 Z" /> 29 <path id="rFoot" d="M 65,77 C 54,77 59,65 60,58 60,51 58,42 65,42 c 9,0 4,9 5,16 C 71,65 75,77 65,77 Z" /> 30 <path id="lFoot" d="M 33,77 C 23,77 28,65 29,58 c 0,-7 -2,-16 5,-16 9,0 4,9 5,16 C 40,65 44,77 33,77 Z" /> 31 <path id="body" fill-opacity="1" d="m 50,29 c 8,0 19,1 24,6 6,7 12,11 3,18 -5,4 -8,9 -13,12 -8,2 -7,2 -14,2 C 45,67 42,67 38,66 33,63 28,60 22,52 14,47 17,39 26,33 32,30 42,29 50,29 Z" /> 32 <path id="head" fill="#BBB" fill-opacity="1" d="m 54,62 c -2,0 -2,2 -3,2 -1,0 0,-1 -3,-2 -3,-1 -7,-3 -9,-4 -2,-1 -2,0 -3,-1 -3,-3 -6,-11 -3,-21 3,-7 9,-11 17,-11 9,0 15,6 17,14 -1,0 2,12 -4,19 -1,1 -1,0 -3,0 0,0 -4,3 -7,4 z" /> 33 </g> 34 35 </svg> 36 </div> 37 38<svg id="svgWorld" style="overflow:hidden;" width="1000" height="8000" viewBox="0 0 1000 8000" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><!-- --> 39 40 <g id="walls" fill="#FFFFFF" stroke="none" filter="url(#goo) url(#outline)"><!-- url(#iosShadow) --> 41 42 <g id="wall01"> 43 44 <use href="#wall" fill="#FFFFFF" /> 45 46 <g id="p0" class="pointer" transform="translate(-520, -100)"> 47 <use class="center" href="#pointerCenter" /> 48 <use class="hand" fill="#FFFFFF" href="#pointerFace2Flip" /> 49 </g> 50 51 </g> 52 53 </g> 54 55 <defs> 56 57 <filter id="goo" x="-10%" y="-10%" width="120%" height="120%"> 58 <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="feBlur" /> 59 <feColorMatrix in="feBlur" mode="matrix" values="1 1 1 1 0 1 1 1 1 0 1 1 1 1 0 0 0 0 19 -9" /> 60 </filter> 61 62 <filter id="outline" x="-10%" y="-10%" width="120%" height="120%"> 63 64 <feColorMatrix in="SourceAlpha" values="1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 0" result="mx" /> 65 <feMorphology in="mx" operator="dilate" radius="2" result="mor1" /> 66 <feComposite in="mor1" in2="SourceGraphic" operator="out" result="com1" /> 67 <feOffset in="com1" dx="0" dy="0" /> 68 69 </filter> 70 <!-- <filter id="outline2" filterUnits="userSpaceOnUse" x="-20%" y="-20%" width="140%" height="140%"> 71 <feMorphology operator="dilate" in="SourceAlpha" radius="4" result="e1" /> 72 <feMorphology operator="dilate" in="SourceAlpha" radius="0" result="e2" /> 73 <feComposite in="e1" in2="e2" operator="xor" result="outline"/> 74 <feColorMatrix type="matrix" in="outline" values="1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0" result="outline2"/> 75 <feComposite in="outline2" in2="SourceAlpha" operator="out" result="output" /> 76 77 </filter> --> 78 79 <filter id="iosShadow" x="0%" y="0%"> 80 <feOffset in="SourceGraphic" result="offset-graphic" dx="0" dy="0"/> 81 <feGaussianBlur in="offset-graphic" result="blur-graphic" stdDeviation="2"/> 82 <feColorMatrix in="blur-graphic" result="color-graphic" type="matrix" values=" 83 1 1 1 1 0 84 1 1 1 1 0 85 1 1 1 1 0 86 0 0 0 1 0"/> 87 <feMerge> 88 <feMergeNode in="color-graphic"/> 89 <feMergeNode in="SourceGraphic"/> 90 </feMerge> 91 </filter> 92 93 94 </defs> 95 96</svg> 97 98 99<svg width="0" height="0" style="position:absolute" role="none" xmlns="http://www.w3.org/2000/svg"> 100 <defs> 101 102 <path id="wall" d="M -730,-51 V 8050 H 121 C 89,7823 42,7557 51,7330 c 10,-248 49,-637 57,-900 -5,-262 -69,-692 -52,-1032 3,-353 20,-635 22,-984 2,-251 -45,-629 -41,-878 5,-327 67,-632 68,-869 1,-205 -38,-574 -42,-859 -5,-319 51,-609 47,-756 C 105,889 36,637 38,438 27,265 48,224 60,-51 Z m 1719,0 c -61,134 -87,342 -82,501 -1,172 43,370 40,555 -5,248 -64,604 -64,799 -3,212 56,652 49,936 -12,339 -60,498 -56,831 4,336 61,420 47,851 -13,411 -62,779 -67,1000 -6,266 75,681 85,1016 -4,356 -65,709 -64,953 -13,330 32,444 66,659 h 787 V -51 Z" /> 103 104 <path id="pointerCenter" fill-opacity="0" d="M 1000,500 C 1000,632 947,759 854,853 760,947 633,999 500,999 367,999 240,947 146,853 53,759 0,632 0,500 0,368 53,241 146,147 c 195,-195 512,-195 707,0 C 947,241 1000,368 1000,500 Z" /> 105 <path id="pointerHand" d="m 414,194 c 13,15 31,31 41,25 13,-7 -17,-119 -7,-120 23,-1 25,116 47,116 12,0 24,-114 41,-115 14,5 -12,117 -1,123 7,5 19,-16 21,-21 8,-21 34,-50 37,-46 9,10 -8,35 -23,77 -7,19 -64,47 -63,75 C 511,385 492,620 826,657 550,732 79,671 170,661 486,626 466,370 466,305 c 0,-35 -37,-57 -57,-80 -26,-29 -32,-69 -26,-71 8,-3 31,40 31,40 z" /> 106 <path id="pointerFace" d="m 399,201 c 9,-19 -7,-27 14,-41 16,-10 12,-12 19,-12 11,0 17,5 28,18 6,7 -16,21 -7,30 2,2 6,2 13,-1 11,-5 35,3 23,33 -2,4 9,13 13,10 9,-6 12,-10 21,-17 11,-9 38,17 31,26 -12,16 12,25 20,15 10,-12 17,18 40,4 7,-4 8,-9 5,-13 -7,-7 -9,-31 -25,-27 -10,2 -26,-10 -18,-14 10,-4 1,-20 -13,-20 -14,0 -10,-14 -9,-21 2,-14 15,0 16,-13 1,-11 -13,-6 -8,-21 3,-7 -3,-14 4,-17 15,-7 17,-4 17,-11 C 584,98 567,100 564,94 c -4,-10 -10,-15 14,-18 16,-2 41,7 71,-2 52,-16 53,16 76,9 45,-13 41,12 61,22 32,16 62,35 61,54 0,6 4,19 9,27 15,22 -14,24 -10,35 5,16 -1,35 2,52 C 850,285 865,284 884,282 c 15,-1 26,-1 29,7 9,21 -38,55 -6,55 11,0 -3,-18 10,-12 27,12 -22,40 -35,9 C 878,331 874,328 886,317 c 33,-31 -36,-11 -35,-5 3,16 -2,52 0,63 1,5 14,2 17,7 C 881,400 858,482 858,505 c 2,100 19,227 -188,213 -6,22 -357,47 -356,24 2,-37 138,-261 82,-288 -37,-18 -47,-80 -28,-111 5,-9 5,-15 -2,-25 -14,-19 -16,-53 -5,-75 19,-11 28,-21 38,-42 z m 239,-47 c 17,0 35,55 77,51 29,-3 26,-38 6,-56 -9,-8 -46,-16 -64,-14 -17,2 -38,18 -19,19 z" /> 107 <path id="pointerFace2" d="M 613,516 C 585,513 543,473 511,455 478,436 384,614 353,682 321,753 204,643 201,630 196,609 470,364 465,346 c -16,-55 -33,-91 -41,-140 -5,-20 1,-35 19,-46 12,-6 16,0 14,13 -3,13 -17,7 -14,17 5,15 12,-6 24,5 6,6 -11,18 -1,22 15,5 12,-20 22,-18 11,2 -5,27 12,27 19,-2 -5,-25 12,-33 15,-8 12,30 26,18 9,-6 -17,-25 -5,-33 13,-9 17,25 28,10 7,-13 -21,-20 -12,-30 10,-12 19,17 26,2 5,-7 -19,-14 -15,-24 5,-10 23,9 25,-1 5,-7 -21,-6 -19,-17 5,-24 110,0 110,0 -15,25 30,16 32,3 2,-8 5,-7 8,2 35,64 111,67 113,92 31,332 -211,305 -216,304 z M 809,253 c 3,-16 -5,-22 -22,-33 -57,-43 -129,-64 -130,2 2,36 22,65 50,69 19,4 97,-15 100,-37 z" /> 108 <path id="pointerFace3" d="m 713,119 c -25,-5 -85,12 -97,8 -7,-2 -50,-14 -54,-8 -11,17 3,18 4,24 3,14 -21,18 -5,28 19,13 -15,39 15,43 29,3 1,22 15,29 17,9 35,18 15,35 -14,12 61,40 -4,38 -17,0 -38,4 -38,-6 -3,-34 3,-45 -30,-30 -13,6 -14,-28 -14,-35 3,-45 -54,6 -44,-27 12,-39 -32,3 -34,-15 -3,-23 44,-35 -6,-38 -7,-19 11,-15 10,-31 0,-8 -5,-25 -15,-20 -64,32 -26,101 -61,136 -45,44 -100,38 -173,75 -71,36 -90,230 -74,258 171,69 341,75 512,17 -27,-33 -62,-90 -54,-133 0,0 20,-42 43,-61 16,-15 64,-15 110,-56 26,-24 23,44 9,46 -9,2 -18,31 -26,28 -88,-27 -79,15 -62,31 20,19 63,31 106,26 38,-4 66,-14 83,-48 23,-48 15,-124 -3,-181 -11,-33 -55,-71 -62,-85 -14,-24 -53,-47 -66,-48 z m -42,51 c 11,11 26,15 44,18 15,3 26,9 32,20 3,7 1,17 -5,22 -12,9 -29,-5 -40,-11 -15,-8 -31,-20 -33,-24 2,-9 1,-17 1,-26 z" /> 109 110 <use id="pointerHandFlip" href="#pointerHand" /> 111 <path id="pointerFaceFlip" d="m 602,201 c -9,-19 7,-27 -14,-41 -16,-10 -12,-12 -19,-12 -11,0 -17,5 -28,18 -6,7 16,21 7,30 -2,2 -6,2 -13,-1 -11,-5 -35,3 -23,33 2,4 -9,13 -13,10 -9,-6 -12,-10 -21,-17 -11,-9 -38,17 -31,26 12,16 -12,25 -20,15 -10,-12 -17,18 -40,4 -7,-4 -8,-9 -5,-13 7,-7 9,-31 25,-27 10,2 26,-10 18,-14 -10,-4 -1,-20 13,-20 14,0 10,-14 9,-21 -2,-14 -15,0 -16,-13 -1,-11 13,-6 8,-21 -3,-7 3,-14 -4,-17 -15,-7 -17,-4 -17,-11 -1,-11 16,-9 19,-15 4,-10 10,-15 -14,-18 -16,-2 -41,7 -71,-2 -52,-16 -53,16 -76,9 -45,-13 -41,12 -61,22 -32,16 -62,35 -61,54 0,6 -4,19 -9,27 -15,22 14,24 10,35 -5,16 1,35 -2,52 -2,12 -17,11 -36,9 -15,-1 -26,-1 -29,7 -9,21 38,55 6,55 -11,0 3,-18 -10,-12 -27,12 22,40 35,9 4,-10 8,-13 -4,-24 -33,-31 36,-11 35,-5 -3,16 2,52 0,63 -1,5 -14,2 -17,7 -13,18 10,100 10,123 -2,100 -19,227 188,213 6,22 357,47 356,24 -2,-37 -138,-261 -82,-288 37,-18 47,-80 28,-111 -5,-9 -5,-15 2,-25 14,-19 16,-53 5,-75 -19,-11 -28,-21 -38,-42 z m -239,-47 c -17,0 -35,55 -77,51 -29,-3 -26,-38 -6,-56 9,-8 46,-16 64,-14 17,2 38,18 19,19 z" /> 112 <path id="pointerFace2Flip" d="m 386,516 c 28,-3 70,-43 102,-61 33,-19 127,159 158,227 32,71 149,-39 152,-52 5,-21 -269,-266 -264,-284 16,-55 33,-91 41,-140 5,-20 -1,-35 -19,-46 -12,-6 -16,0 -14,13 3,13 17,7 14,17 -5,15 -12,-6 -24,5 -6,6 11,18 1,22 -15,5 -12,-20 -22,-18 -11,2 5,27 -12,27 -19,-2 5,-25 -12,-33 -15,-8 -12,30 -26,18 -9,-6 17,-25 5,-33 -13,-9 -17,25 -28,10 -7,-13 21,-20 12,-30 -10,-12 -19,17 -26,2 -5,-7 19,-14 15,-24 -5,-10 -23,9 -25,-1 -5,-7 21,-6 19,-17 -5,-24 -110,0 -110,0 15,25 -30,16 -32,3 -2,-8 -5,-7 -8,2 -35,64 -111,67 -113,92 -31,332 211,305 216,304 z m -196,-263 c -3,-16 5,-22 22,-33 57,-43 129,-64 130,2 -2,36 -22,65 -50,69 -19,4 -97,-15 -100,-37 z" /> 113 <path id="pointerFace3Flip" d="m 289,119 c 25,-5 85,12 97,8 7,-2 50,-14 54,-8 11,17 -3,18 -4,24 -3,14 21,18 5,28 -19,13 15,39 -15,43 -29,3 -1,22 -15,29 -17,9 -35,18 -15,35 14,12 -61,40 4,38 17,0 38,4 38,-6 3,-34 -3,-45 30,-30 13,6 14,-28 14,-35 -3,-45 54,6 44,-27 -12,-39 32,3 34,-15 3,-23 -44,-35 6,-38 7,-19 -11,-15 -10,-31 0,-8 5,-25 15,-20 64,32 26,101 61,136 45,44 100,38 173,75 71,36 90,230 74,258 -171,69 -341,75 -512,17 27,-33 62,-90 54,-133 0,0 -20,-42 -43,-61 -16,-15 -64,-15 -110,-56 -26,-24 -23,44 -9,46 9,2 18,31 26,28 88,-27 79,15 62,31 -20,19 -63,31 -106,26 -38,-4 -66,-14 -83,-48 -23,-48 -15,-124 3,-181 11,-33 55,-71 62,-85 14,-24 51,-48 66,-48 z m 42,51 c -11,11 -26,15 -44,18 -15,3 -26,9 -32,20 -3,7 -1,17 5,22 12,9 29,-5 40,-11 15,-8 31,-20 33,-24 -2,-9 -1,-17 -1,-26 z" /> 114 115 </defs> 116</svg> 117 <script src='https://unpkg.com/gsap@3/dist/gsap.min.js'></script> 118<script src='https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js'></script> 119<script src='https://cdn.jsdelivr.net/npm/onscreen@1.4.0/dist/on-screen.umd.min.js'></script><script src="./script.js"></script> 120 121 </body> 122 123</html> 124

The HTML file forms the structure of the Halloween scene. It contains:

A main container (div class="container") that centers the content both vertically and horizontally.

A heading (

Valley of Ghouls

) — the glowing main title of the page.

Two ghost elements (div class="ghost") representing the floating ghosts that animate independently.

A button (button id="next") that triggers the theme transformation via JavaScript.

This HTML acts as the base layout — simple, clean, and designed for the CSS and JS to add life to it.


CSS Code


1* { 2 box-sizing: border-box; 3} 4 5html, body { 6 margin:0; 7 background:#000; 8} 9body { 10 display: flex; 11} 12#dataText { 13 position: fixed; 14 left: 0px; 15 top: 0px; 16 z-index: 100; 17 padding: 50px; 18 width: 100vw; 19} 20#walkerWrapper { 21 22 position: fixed; 23 display: block; 24 left: 0px; 25 top: 55vh; 26 top: calc( 50vh - 40px ); 27 z-index: 100; 28 padding: 0px; 29 width: 100vw; 30 text-align: center; 31 color: #FFF; 32 overflow: visible; 33 34 #walker { 35 position: relative; 36 display: block; 37 width: 100px; 38 height: 100px; 39 border-radius: 50%; 40 margin: auto; 41 overflow: visible; 42 43 } 44 #walker.up, #walker.down { 45 #lFoot { animation: walkingAnim1 1s infinite; } 46 #rFoot { animation: walkingAnim2 1s infinite; } 47 } 48} 49#weapon01Wrapper, #weapon02Wrapper { 50 overflow: visible; 51} 52 53 54@keyframes walkingAnim1 { 55 0% { transform: translateY(-5px); } 56 50% { transform: translateY(0px); } 57 100% { transform: translateY(-5px); } 58} 59@keyframes walkingAnim2 { 60 0% { transform: translateY(0px); } 61 50% { transform: translateY(-5px); } 62 100% { transform: translateY(0px); } 63} 64 65#svgWorld { 66 position: relative; 67 width: 90%; 68 max-width: 1000px; 69 margin-left: auto; 70 margin-right: auto; 71 height: auto; 72 overflow: hidden; 73 /* will-change: transform, opacity; 74 transform-box: fill-box; */ 75 76 .pointer { 77 pointer-events: none; 78 } 79} 80

The CSS is where the magic and spookiness come to life.

Key styling concepts:

Background Design:

A dark, gradient-based background simulates a Halloween night sky (background: radial-gradient(circle, #111 0%, #000 100%)).

Smooth transitions are added for a glowing, eerie feel.

Typography & Title Effects:

The title text uses text-shadow to create a neon glow (orange by default).

The font is set to something clean but bold for visibility.

The title subtly pulses using the @keyframes glow animation.

Ghost Animation:

Each ghost is styled as a white circular shape with opacity to simulate transparency.

The @keyframes float animation makes them gently move up and down infinitely.

Positioning is absolute, allowing them to hover freely within the screen.

Button Styling:

The “Next” button has a hover effect and matches the Halloween theme (red or orange color).

The rounded corners and glow on hover make it appear interactive and playful.

Responsiveness:

The design remains centered and balanced using flexbox.

Media queries ensure consistent layout across screen sizes.

Overall, the CSS adds motion, color, and emotion, transforming a static layout into an immersive spooky interface.


Javascipt Code


1let select = e => document.querySelector(e); 2let selectAll = e => document.querySelectorAll(e); 3 4const svgWorld = select("#svgWorld"), 5feOffsetAll = selectAll("#outline feOffset[in='com2']"), 6wall01 = select("#wall01"), 7handType = ["#pointerHand", "#pointerFace", "#pointerHand", "#pointerFace2", "#pointerHand", "#pointerFace3"], 8handTypeSpace = [130, 260, 130, 260, 130, 260], 9pointElm = select("#p0"), 10numReq = 22; 11 12let oldPageYOffset = 0; 13let scrollTimer = null; 14let isTurning = false; 15 16window.addEventListener("scroll", (event) => { 17 18 if( scrollTimer !== null ) 19 { 20 21 clearTimeout(scrollTimer); 22 23 newPageYOffset = window.pageYOffset; 24 if( isTurning == false ) 25 { 26 if( oldPageYOffset - newPageYOffset < 0 ) 27 { 28 walker.classList.remove("up"); 29 walker.classList.add("down"); 30 gsap.to(wMain, { rotate: 0, transformOrigin: "50% 50%" }); 31 } 32 else if( oldPageYOffset - newPageYOffset > 0 ) 33 { 34 walker.classList.add("up"); 35 walker.classList.remove("down"); 36 gsap.to(wMain, { rotate: 180, transformOrigin: "50% 50%" }); 37 } 38 } 39 oldPageYOffset = newPageYOffset; 40 41 } 42 scrollTimer = setTimeout(function() { 43 walker.classList.remove("up"); 44 walker.classList.remove("down"); 45 }, 250); 46 47}); 48 49/* Random ghoul generator */ 50let ySum = -150; 51for( let index = 1; index < numReq; index++ ) 52{ 53 54 let handTypeStr = gsap.utils.random(handType); 55 let handTypeIndex = handType.indexOf(handTypeStr); 56 57 let point = pointElm.cloneNode(true); 58 point.id = "p" + index; 59 let x = 520; 60 ySum += handTypeSpace[handTypeIndex]; 61 let flipPath = ""; 62 if( index % 2 === 0 ) 63 { 64 x = -520; 65 flipPath = "Flip"; 66 } 67 point.setAttribute("transform", "translate("+x+", "+ySum+")"); 68 point.querySelector(".hand").setAttribute( "href", handTypeStr + flipPath ); 69 wall01.appendChild( point ); 70 71} 72pointElm.setAttribute("transform", "translate(-520, -100)"); 73gsap.set(pointElm, { rotate: 90, transformOrigin: "50% 50%" }); 74 75ySum += 1500; 76svgWorld.setAttribute("height", ySum ); 77svgWorld.setAttribute("viewBox", "0 0 1000 " + ySum ); 78const pointers = selectAll(".pointer"); 79 80 81/* rotate and move pointers towards walker */ 82 83rotatePointers(); 84addEventListener("scroll", (event) => { rotatePointers(); }); 85 86function rotatePointers() { 87 88 /* get the walker position */ 89 var elem = (document.compatMode === "CSS1Compat") ? 90 document.documentElement : 91 document.body; 92 93 var svgOffsets = svgWorld.getBoundingClientRect(); 94 95 let centerX = svgOffsets.width / 2; 96 let centerY = Math.round( window.scrollY + ( elem.clientHeight / 2 ) ); 97 98 let distanceMem01 = 6000, 99 distanceMem02 = 6000, 100 degreesMem01 = 0, 101 degreesMem02 = 0; 102 103 /* set all on screen pointer positions based on walker */ 104 pointers.forEach( ( pointer, index ) => { 105 106 var center = pointer.querySelector(".center"); 107 var hand = pointer.querySelector(".hand"); 108 109 var offsets = center.getBoundingClientRect(); 110 111 if( OnScreen.check(center) ) 112 { 113 114 var halfWidth = offsets.width / 2; 115 var halfHeight = offsets.height / 2; 116 var pointerX = Math.round( offsets.left - svgOffsets.left + halfWidth ); 117 var pointerY = Math.round( offsets.top + window.scrollY + halfHeight ); 118 119 var xAxis = ( centerX - pointerX ) * 1; 120 var yAxis = ( centerY - pointerY ) * -1; 121 122 let radians = Math.atan2( xAxis, yAxis ); 123 let degrees = ( radians * 180 / Math.PI ).toFixed(2); 124 let distance = Math.hypot( xAxis, yAxis ).toFixed(2); 125 let distanceAbs = Math.abs( yAxis ); 126 127 gsap.to( pointer, 0.5, { rotation: degrees, transformOrigin:"50% 50%" } ); 128 if( distanceAbs < 300 ) 129 { 130 let inverseDistance = halfWidth * distanceAbs / 300; 131 gsap.to( hand, 0.5, { y: inverseDistance } ); 132 } 133 else 134 gsap.to( hand, 0.5, { y: halfWidth } ); 135 136 /* get two closest pointers */ 137 if( distanceAbs < distanceMem01 ) 138 { 139 distanceMem02 = distanceMem01; 140 degreesMem02 = degreesMem01; 141 distanceMem01 = distanceAbs; 142 degreesMem01 = degrees; 143 } 144 else if( distanceAbs < distanceMem02 ) 145 { 146 distanceMem02 = distanceAbs; 147 degreesMem02 = degrees; 148 } 149 150 } 151 152 }); 153 154 /* rotate weapons towards nearest pointers */ 155 degreesMem01 = ( degreesMem01 + 180 ) % 360; 156 degreesMem02 = ( degreesMem02 + 180 ) % 360; 157 gsap.to( "#weapon01Wrapper", 0.5, { rotation: degreesMem01, transformOrigin:"50% 50%" } ); 158 gsap.to( "#weapon02Wrapper", 0.5, { rotation: degreesMem02, transformOrigin:"50% 50%" } ); 159 160 161} 162 163/* animate walker at the bottom of page */ 164ScrollTrigger.create({ 165 trigger: svgWorld, 166 start: 'bottom-=100px bottom', 167 onEnter: function () { 168 isTurning = true; 169 walker.classList.remove("up"); 170 walker.classList.remove("down"); 171 gsap.timeline() 172 //.set( "#weapon01Wrapper", { rotation: 90, transformOrigin:"50% 50%" } ) 173 //.set( "#weapon02Wrapper", { rotation: -90, transformOrigin:"50% 50%" } ) 174 .to( wMain, { rotate: 0, transformOrigin: "50% 50%" }) 175 .to( "#weapon01Wrapper", 1, { rotation: 90, transformOrigin:"50% 50%" } ) 176 .to( "#weapon02Wrapper", 1, { rotation: -90, transformOrigin:"50% 50%" }, "<" ) 177 .fromTo( ["#weapon01Wrapper","#weapon02Wrapper"], 1, { y: -15 }, { y: 0, duration: 0.25, easing: "none", repeat :3, repeatDelay: 0, onComplete() { isTurning = false; } }); 178 }, 179}); 180

The JavaScript introduces interactivity and dynamic transformation to the page.

Main logic:

Selecting Elements:

The script selects the main title, ghosts, and the button using document.querySelector or getElementById.

Event Handling:

When the “Next” button is clicked, an event listener triggers a function.

Dynamic Effects:

The background gradient and text glow color change from orange to cyan, giving a “midnight magic” transition.

The ghosts’ animations speed up or their opacity and glow colors adjust to match the new theme.

The button text changes to “Happy Haunting!” as a final interactive message.

Transitions:

Smooth CSS transitions (e.g., transition: background 2s) help the color changes feel natural rather than abrupt.

In short, the JavaScript adds user-triggered animation and atmosphere change, making the website interactive and alive — not just a static Halloween display.

Love this component?

Explore more components and build amazing UIs.

View All Components