Let’s create a Ghibli-style Moving Castle using HTML, CSS, and JavaScript. Inspired by the magical world of Studio Ghibli, this project will feature an animated castle that moves dynamically, creating a whimsical and immersive visual effect.
We’ll use HTML to structure the castle, CSS to style it with a Ghibli-inspired aesthetic, and JavaScript to animate the castle’s movement, bringing it to life with smooth transitions.
Let’s get started on building this enchanting Ghibli-style Moving Castle. Whether you’re a beginner or an experienced developer, this project is a fantastic way to practice animation techniques and create a visually stunning experience ✨🏰
HTML :
This HTML code creates a visually rich Ghibli-style moving castle animation using multiple layered images for different castle parts, clouds, and background elements. It structures the castle with divs and images, adding legs, chimneys, wings, and other details. The page links an external CSS file for styling and animations, and it includes JavaScript (jQuery and GSAP) for dynamic interactions. A toggle button allows mouse control activation, and a loading screen is shown initially.
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>Ghibli Style Moving Castle | @codingstella</title> <link rel="stylesheet" href="./style.css"> </head> <body> <div class="container"> <img class="background" src="/Image/background.jpg"> <img class="cloud-bg" src="/Image/cloud-bg.png"> <img class="cloud-bg2" src="/Image/cloud-bg.png"> <div class="castle-container"> <div class="castle"> <div class="brleg"> <img class="brfoot" src="/Image/brfoot.png" /> <img class="brbottom" src="/Image/brbottom.png" /> </div> <div class="frleg"> <img class="frfoot" src="/Image/frfoot.png" /> <img class="frbottom" src="/Image/frbottom.png" /> </div> <img class="chimney3" src="/Image/chimney3.png" /> <img class="treehouse" src="/Image/treehouse.png" /> <div class="houses-group"> <img class="point6" src="/Image/point6.png" /> <img class="point5" src="/Image/point5.png" /> <img class="point4" src="/Image/point4.png" /> <img class="houses" src="/Image/houses.png" /> </div> <img class="chimney2" src="/Image/chimney2.png" /> <img class="chimney1" src="/Image/chimney1.png" /> <img class="wing" src="/Image/wing.png" /> <div class="mound-group"> <img class="antenna" src="/Image/antenna.png" /> <img class="point3" src="/Image/point3.png" /> <img class="point2" src="/Image/point2.png" /> <img class="point1" src="/Image/point1.png" /> <img class="mound" src="/Image/mound.png" /> </div> <img class="wind" src="/Image/wind.png" /> <img class="cannon" src="/Image/cannon.png" /> <img class="main" src="/Image/main.png" /> <div class="blleg"> <div class="blbottom-group"> <img class="blfoot" src="/Image/flfoot.png" /> <img class="blbottom" src="/Image/flbottom.png" /> </div> <img class="bltop" src="/Image/fltop.png" /> </div> <img class="blcover" src="/Image/blcover.png" /> <img class="knob" src="/Image/knob.png" /> <img class="tele" src="/Image/tele.png" /> <img class="telecover" src="/Image/telecover.png" /> <div class="flleg"> <div class="flbottom-group"> <img class="flfoot" src="/Image/flfoot.png" /> <img class="flbottom" src="/Image/flbottom.png" /> </div> <img class="fltop" src="/Image/fltop.png" /> </div> <img class="flcover" src="/Image/flcover.png" /> </div> </div> <img class="foreground" src="/Image/foreground.png"> <div class="clouds"> <img class="cloud-shadow1" src="/Image/cloud_shadow-1.png"> <img class="cloud-shadow2" src="/Image/cloud_shadow-1.png"> <img class="cloud-shadow3" src="/Image/cloud_shadow-1.png"> <img class="cloud1" src="/Image/cloud-1.png"> <img class="cloud2" src="/Image/cloud-1.png"> <img class="cloud3" src="/Image/cloud-2.png"> <img class="cloud4" src="/Image/cloud-1.png"> <img class="cloud5" src="/Image/cloud-2.png"> </div> </div> <div class="control-toggle">Toggle mouse controls</div> <div class="load-gate">Loading...</div> <!-- partial --> <script src='//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script> <script src='//cdnjs.cloudflare.com/ajax/libs/gsap/1.14.2/TweenMax.min.js'></script><script src="./script.js"></script> </body> </html>
CSS :
This CSS creates a 3D animated castle scene with clouds and various elements using absolute positioning, transform-origin
, and rotateZ()
for depth. The .container
holds the scene at the bottom, .control-toggle
adds interactivity, and .load-gate
serves as a loading overlay.
html, body { height: 100%; margin: 0; background: #2294b3; overflow: hidden; } .container { padding-top: 62.5%; position: absolute; width: 100%; bottom: 0; left: 0; } .container.active { cursor: ew-resize; } img, .mound-group, .houses-group, .flbottom-group, .blbottom-group, .brleg, .frleg, .blleg, .flleg { position: absolute; transform-style: preserve-3d; } .castle-container { position: absolute; left: 100%; bottom: 0%; } .castle { position: absolute; top: 0; left: 0; width: 600px; height: 750px; perspective: 1000px; transform-origin: 50% 70%; transform: translate(-50%, -70%) rotateZ(9deg); } .brleg { left: 400px; top: 625px; transform-origin: 10px -10px; transform: rotateZ(0deg); } .brfoot { left: -18px; top: 82px; transform-origin: 56% 44%; transform: rotateZ(0deg); } .brbottom { } .frleg { left: 240px; top: 653px; transform-origin: 8px -10px; transform: rotateZ(0deg); } .frfoot { left: -18px; top: 51px; transform-origin: 56% 44%; transform: rotateZ(0deg); } .frbottom { } .chimney3 { left: 400px; top: 30px; transform-origin: 45% 120%; transform: rotateZ(0deg); } .houses-group { left: 305px; top: 130px; transform-origin: -50px 300px; transform: rotateZ(1deg); } .point6 { left: 84px; top: 19px; transform-origin: 40% 120%; transform: rotateZ(0deg); } .point5 { left: 70px; top: -23px; transform-origin: -40% 200%; transform: rotateZ(0deg); } .point4 { left: 40px; top: -17px; transform-origin: 0% 100%; transform: rotateZ(0deg); } .houses { } .treehouse { left: 220px; top: 10px; transform-origin: 50% 150%; transform: rotateZ(0deg); } .chimney2 { left: 430px; top: 120px; transform-origin: 0% 90%; transform: rotateZ(0deg); } .chimney1 { left: 420px; top: 90px; transform-origin: -10% 90%; transform: rotateZ(0deg); } .wing { left: 420px; top: 370px; transform-origin: 0% 50%; transform: rotateZ(0deg); } .antenna { left: -100px; top: 90px; transform-origin: 100% 65%; transform: rotateZ(0deg); } .mound-group { left: 115px; top: 110px; transform-origin: 110px 220px; transform: rotateZ(0deg); } .point3 { left: 125px; top: -13px; transform-origin: 50% 400%; transform: rotateZ(0deg); } .point2 { left: 50px; top: -22px; transform-origin: 120% 200%; transform: rotateZ(0deg); } .point1 { left: 4px; top: 55px; transform-origin: 150% 150%; transform: rotateZ(0deg); } .mound { } .wind { left: 400px; top: 260px; transform-origin: 0% 90%; transform: rotateZ(0deg); } .cannon { left: 30px; top: 460px; transform-origin: 100% 60%; transform: rotateZ(0deg); } .main { left: 80px; top: 230px; transform-origin: 50% 50%; transform: rotateZ(0deg); } .blleg { left: 410px; top: 615px; transform-origin: 10px 15px; transform: rotateZ(0deg); } .blbottom-group { left: 0px; top: 60px; transform-origin: 10px 0px; transform: rotateZ(0deg); } .blfoot { left: -19px; top: 68px; transform-origin: 56% 44%; transform: rotateZ(0deg); } .blbottom { } .bltop { } .blcover { left: 360px; top: 573px; } .knob { left: 214px; top: 524px; transform-origin: 30% 63%; transform: rotateZ(0deg); } .tele { left: 90px; top: 430px; transform-origin: 90% 50%; transform: rotateZ(0deg); } .telecover { left: 161px; top: 399px; } .flleg { left: 250px; top: 615px; transform-origin: 10px 15px; transform: rotateZ(0deg); } .flbottom-group { left: 0px; top: 60px; transform-origin: 10px 0px; transform: rotateZ(0deg); } .flfoot { left: -19px; top: 68px; transform-origin: 56% 44%; transform: rotateZ(0deg); } .flbottom { } .fltop { } .flcover { left: 244px; top: 567px; } .foreground { position: absolute; bottom: 0; left: 0; width: 100%; } .background { position: absolute; bottom: 25.5%; left: 0; width: 100%; } .cloud-bg { bottom: 17%; width: 80%; right: 100%; } .cloud-bg2 { bottom: 17%; width: 80%; right: 100%; } .cloud-shadow1 { bottom: 43%; right: 100%; width: 80%; transform: rotate(5deg); } .cloud1 { bottom: 30%; right: 100%; width: 80%; } .cloud-shadow2 { bottom: 12%; left: 36%; width: 80%; transform: rotate(5deg); } .cloud-shadow3 { bottom: 31%; left: -30%; width: 80%; transform: rotate(5deg); } .cloud2 { bottom: 46%; left: -29%; width: 80%; } .cloud3 { bottom: 38%; left: 17%; width: 80%; } .cloud4 { bottom: 18%; left: -18%; width: 80%; } .cloud5 { bottom: 8%; left: 40%; width: 80%; } .control-toggle { position: absolute; top: 0; left: 0; padding: 10px 20px; background: rgba(255, 255, 255, 1); font-family: sans-serif; text-transform: uppercase; font-size: 10px; letter-spacing: 0.05em; opacity: 0.1; cursor: pointer; } .control-toggle:hover { opacity: 1; } .load-gate { position: absolute; top: 0; right: 0; bottom: 0; left: 0; background: #fff; font-family: sans-serif; text-transform: uppercase; font-size: 10px; letter-spacing: 0.2em; padding: 20px; }
JavaScript:
This JavaScript uses GSAP to animate a 3D castle scene with moving clouds, walking legs, and interactive speed control based on mouse movement. It adapts to screen size and creates smooth animations for various elements.
window.addEventListener("DOMContentLoaded",() => { const clock = new BouncyBlockClock(".clock"); }); class BouncyBlockClock { constructor(qs) { this.el = document.querySelector(qs); this.time = { a: [], b: [] }; this.rollClass = "clock__block--bounce"; this.digitsTimeout = null; this.rollTimeout = null; this.mod = 0 * 60 * 1000; this.loop(); } animateDigits() { const groups = this.el.querySelectorAll("[data-time-group]"); Array.from(groups).forEach((group,i) => { const { a, b } = this.time; if (a[i] !== b[i]) group.classList.add(this.rollClass); }); clearTimeout(this.rollTimeout); this.rollTimeout = setTimeout(this.removeAnimations.bind(this),900); } displayTime() { // screen reader time const timeDigits = [...this.time.b]; const ap = timeDigits.pop(); this.el.ariaLabel = `${timeDigits.join(":")} ${ap}`; // displayed time Object.keys(this.time).forEach(letter => { const letterEls = this.el.querySelectorAll(`[data-time="${letter}"]`); Array.from(letterEls).forEach((el,i) => { el.textContent = this.time[letter][i]; }); }); } loop() { this.updateTime(); this.displayTime(); this.animateDigits(); this.tick(); } removeAnimations() { const groups = this.el.querySelectorAll("[data-time-group]"); Array.from(groups).forEach(group => { group.classList.remove(this.rollClass); }); } tick() { clearTimeout(this.digitsTimeout); this.digitsTimeout = setTimeout(this.loop.bind(this),1e3); } updateTime() { const rawDate = new Date(); const date = new Date(Math.ceil(rawDate.getTime() / 1e3) * 1e3 + this.mod); let h = date.getHours(); const m = date.getMinutes(); const s = date.getSeconds(); const ap = h < 12 ? "AM" : "PM"; if (h === 0) h = 12; if (h > 12) h -= 12; this.time.a = [...this.time.b]; this.time.b = [ (h < 10 ? `0${h}` : `${h}`), (m < 10 ? `0${m}` : `${m}`), (s < 10 ? `0${s}` : `${s}`), ap ]; if (!this.time.a.length) this.time.a = [...this.time.b]; } }
In conclusion, creating a Ghibli-style Moving Castle using HTML, CSS, and JavaScript has been a magical and creative experience. By combining structure, styling, and animations, we’ve crafted a whimsical moving castle that captures the charm of Studio Ghibli’s animations.
If you run into any hiccups with your project, worry not. You can easily grab the source code for this project. Just hit the Download button to get started on your coding adventure. Happy coding!