Close Menu

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    How to make Kinetic Morph Loader using HTML CSS & JavaScript

    1 November 2026

    How to make Love Letter Animation using HTML CSS & JavaScript

    29 October 2026

    Navigation Menu Tab Vol. 1 using HTML, CSS & JavaScript

    24 October 2026
    Facebook X (Twitter) Instagram YouTube Telegram Threads
    Coding StellaCoding Stella
    • Home
    • Blog
    • HTML & CSS
      • Login Form
    • JavaScript
    • Hire us!
    Coding StellaCoding Stella
    Home » How to make Kinetic Morph Loader using HTML CSS & JavaScript
    JavaScript

    How to make Kinetic Morph Loader using HTML CSS & JavaScript

    Coding StellaBy Coding Stella1 November 2026No Comments8 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Email WhatsApp Copy Link

    Let’s create a Kinetic Morph Loading Animation using HTML, CSS, and JavaScript – a modern preloader featuring four geometric tiles that rotate, move, and morph into different shapes while a percentage counter and progress bar smoothly track the loading progress.

    • HTML: Create a preloader section containing four animated geometric tiles, a percentage counter, a progress bar, and dynamic loading status text. Also add a hidden white page with a greeting message that appears after loading completes.
    • CSS: Style the preloader with a minimal black background, white and orange accents, custom fonts, and smooth animations. Use CSS keyframes to rotate and morph the four tiles, create a progress bar design, and add a circular reveal transition to display the final page.
    • JavaScript: Control the loading animation for 4.5 seconds using requestAnimationFrame, smoothly update the percentage counter and progress bar, change loading statuses, and reveal the final page once loading reaches 100%. Also allow users to replay the animation using the Space or R key.

    This project teaches how to build a creative and modern preloader using HTML, CSS, and JavaScript by combining geometric animations, smooth progress tracking, dynamic text updates, and cinematic page reveal transitions.

    HTML :

    HTML: Creates the structure of the preloader, including the animated tiles, percentage counter, progress bar, loading status, and the final greeting message.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Kinetic Morph Loading Animtion | @coding.stella</title>
      <link rel="stylesheet" href="style.css">
    </head>
    
    <body>
    
      <!-- Preloader -->
      <div id="km-preloader" role="progressbar" aria-label="Loading" aria-live="polite">
        <div class="km-container">
          <div class="km-stage-wrapper">
            <span class="km-reticle km-reticle-tl"></span>
            <span class="km-reticle km-reticle-tr"></span>
            <span class="km-reticle km-reticle-bl"></span>
            <span class="km-reticle km-reticle-br"></span>
            <div class="km-stage">
              <div class="km-tile km-tile-1"></div>
              <div class="km-tile km-tile-2"></div>
              <div class="km-tile km-tile-3"></div>
              <div class="km-tile km-tile-4"></div>
            </div>
          </div>
    
          <div class="km-counter-wrap">
            <span id="km-count" class="km-number">0</span>
            <span class="km-percent">%</span>
          </div>
    
          <div class="km-telemetry-box">
            <div class="km-meta-row">
              <span class="km-title-tag">KINETIC MORPH</span>
              <span id="km-status" class="km-status-tag">INITIALIZING</span>
            </div>
            <div class="km-track-line">
              <div id="km-fill" class="km-fill-line"></div>
            </div>
          </div>
        </div>
      </div>
    
      <!-- Revealed Page -->
      <main id="white-page" class="page">
        <h1 class="greeting">Hey</h1>
      </main>
    
      <script src="script.js"></script>
    </body>
    
    </html>

    CSS :

    CSS: Handles the complete visual design, including the black background, typography, colors, rotating and morphing tile animations, progress bar styling, and the circular reveal transition that displays the white page.

    @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@500;700&family=Space+Grotesk:wght@600;700;800;900&display=swap');
    
    *, *::before, *::after {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    
    html, body {
      background-color: #000000;
      min-height: 100vh;
      overflow: hidden;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Space Grotesk", sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }
    
    /* Preloader Overlay */
    #km-preloader {
      position: fixed;
      inset: 0;
      z-index: 10;
      background-color: #000000;
      display: flex;
      align-items: center;
      justify-content: center;
      user-select: none;
    }
    
    .km-container {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      transition: opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1),
                  transform 0.35s cubic-bezier(0.16, 1, 0.3, 1);
    }
    
    #km-preloader.km-completing .km-container {
      opacity: 0;
      transform: scale(0.92);
    }
    
    /* Geometric Tiles */
    .km-stage-wrapper {
      position: relative;
      padding: 16px;
      margin-bottom: 24px;
    }
    
    .km-reticle {
      position: absolute;
      width: 6px;
      height: 6px;
      border-color: rgba(255, 255, 255, 0.2);
      border-style: solid;
      pointer-events: none;
    }
    
    .km-reticle-tl { top: 0; left: 0; border-width: 1px 0 0 1px; }
    .km-reticle-tr { top: 0; right: 0; border-width: 1px 1px 0 0; }
    .km-reticle-bl { bottom: 0; left: 0; border-width: 0 0 1px 1px; }
    .km-reticle-br { bottom: 0; right: 0; border-width: 0 1px 1px 0; }
    
    .km-stage {
      position: relative;
      width: 72px;
      height: 72px;
    }
    
    .km-tile {
      position: absolute;
      width: 32px;
      height: 32px;
      will-change: transform, border-radius;
      backface-visibility: hidden;
    }
    
    .km-tile-1 {
      top: 0;
      left: 0;
      background-color: #ffffff;
      animation: km-morph-1 3.2s cubic-bezier(0.65, 0, 0.35, 1) infinite;
    }
    
    .km-tile-2 {
      top: 0;
      right: 0;
      background-color: #ff3e00;
      box-shadow: 0 0 20px rgba(255, 62, 0, 0.45);
      animation: km-morph-2 3.2s cubic-bezier(0.65, 0, 0.35, 1) infinite;
    }
    
    .km-tile-3 {
      bottom: 0;
      right: 0;
      background-color: #ffffff;
      animation: km-morph-3 3.2s cubic-bezier(0.65, 0, 0.35, 1) infinite;
    }
    
    .km-tile-4 {
      bottom: 0;
      left: 0;
      background-color: #ffffff;
      animation: km-morph-4 3.2s cubic-bezier(0.65, 0, 0.35, 1) infinite;
    }
    
    #km-preloader.km-completing .km-tile {
      animation-play-state: paused !important;
    }
    
    @keyframes km-morph-1 {
      0% { transform: translate3d(0, 0, 0) rotate(0deg); border-radius: 3px; }
      25% { transform: translate3d(-6px, -6px, 0) rotate(45deg); border-radius: 50%; }
      50% { transform: translate3d(40px, 0, 0) rotate(90deg); border-radius: 6px; }
      75% { transform: translate3d(40px, 40px, 0) rotate(180deg); border-radius: 50%; }
      100% { transform: translate3d(0, 0, 0) rotate(360deg); border-radius: 3px; }
    }
    
    @keyframes km-morph-2 {
      0% { transform: translate3d(0, 0, 0) rotate(0deg); border-radius: 3px; }
      25% { transform: translate3d(6px, -6px, 0) rotate(-45deg); border-radius: 50%; }
      50% { transform: translate3d(0, 40px, 0) rotate(-90deg); border-radius: 6px; }
      75% { transform: translate3d(-40px, 40px, 0) rotate(-180deg); border-radius: 50%; }
      100% { transform: translate3d(0, 0, 0) rotate(-360deg); border-radius: 3px; }
    }
    
    @keyframes km-morph-3 {
      0% { transform: translate3d(0, 0, 0) rotate(0deg); border-radius: 3px; }
      25% { transform: translate3d(6px, 6px, 0) rotate(45deg); border-radius: 50%; }
      50% { transform: translate3d(-40px, 0, 0) rotate(90deg); border-radius: 6px; }
      75% { transform: translate3d(-40px, -40px, 0) rotate(180deg); border-radius: 50%; }
      100% { transform: translate3d(0, 0, 0) rotate(360deg); border-radius: 3px; }
    }
    
    @keyframes km-morph-4 {
      0% { transform: translate3d(0, 0, 0) rotate(0deg); border-radius: 3px; }
      25% { transform: translate3d(-6px, 6px, 0) rotate(-45deg); border-radius: 50%; }
      50% { transform: translate3d(0, -40px, 0) rotate(-90deg); border-radius: 6px; }
      75% { transform: translate3d(40px, -40px, 0) rotate(180deg); border-radius: 50%; }
      100% { transform: translate3d(0, 0, 0) rotate(-360deg); border-radius: 3px; }
    }
    
    /* Tabular Counter & Progress Track */
    .km-counter-wrap {
      display: flex;
      align-items: baseline;
      justify-content: center;
      font-family: 'Space Grotesk', -apple-system, sans-serif;
      line-height: 1;
      margin-bottom: 18px;
    }
    
    .km-number {
      font-size: 76px;
      font-weight: 800;
      color: #ffffff;
      letter-spacing: -2px;
      font-variant-numeric: tabular-nums;
      font-feature-settings: "tnum";
      display: inline-block;
      min-width: 130px;
      text-align: right;
    }
    
    .km-percent {
      font-size: 24px;
      font-weight: 700;
      color: #ff3e00;
      margin-left: 4px;
      transform: translateY(-6px);
    }
    
    .km-telemetry-box {
      width: 320px;
      max-width: 88vw;
      display: flex;
      flex-direction: column;
      gap: 10px;
    }
    
    .km-meta-row {
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 1.5px;
      text-transform: uppercase;
    }
    
    .km-title-tag { color: #666666; }
    .km-status-tag { color: #ff3e00; transition: color 0.2s ease; }
    
    .km-track-line {
      width: 100%;
      height: 2px;
      background-color: #1a1a1a;
      position: relative;
      overflow: visible;
    }
    
    .km-fill-line {
      position: absolute;
      top: 0;
      left: 0;
      height: 100%;
      width: 0%;
      background-color: #ffffff;
      will-change: width;
    }
    
    .km-fill-line::after {
      content: '';
      position: absolute;
      right: -2px;
      top: -2.5px;
      width: 7px;
      height: 7px;
      background-color: #ff3e00;
      border-radius: 50%;
      box-shadow: 0 0 10px #ff3e00;
    }
    
    /* White Page (Circular Iris Reveal) */
    .page {
      position: fixed;
      inset: 0;
      z-index: 20;
      background-color: #ffffff;
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
      padding: 24px;
      clip-path: circle(0% at 50% 50%);
      pointer-events: none;
      will-change: clip-path;
    }
    
    /* Circular Iris Expansion */
    .page.reveal {
      clip-path: circle(150% at 50% 50%);
      pointer-events: auto;
      transition: clip-path 1.05s cubic-bezier(0.85, 0, 0.15, 1);
    }
    
    /* "Hey" Typography with Delayed Fade-in */
    .greeting {
      font-family: 'Space Grotesk', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
      font-size: clamp(80px, 18vw, 160px);
      font-weight: 900;
      color: #000000;
      letter-spacing: -4px;
      line-height: 1;
      user-select: none;
      opacity: 0;
      transform: translateY(16px);
      transition: opacity 1.1s cubic-bezier(0.16, 1, 0.3, 1),
                  transform 1.1s cubic-bezier(0.16, 1, 0.3, 1);
    }
    
    .greeting.fade-in {
      opacity: 1;
      transform: translateY(0);
    }
    

    JavaScript:

    JavaScript: Controls the loading animation for 4.5 seconds, smoothly updates the percentage and progress bar, changes loading statuses, and once loading reaches 100%, hides the preloader and reveals the final page with a fade-in greeting. It also allows replaying the animation using the Space or R key.

    Is this conversation helpful so far?

    (function initCircularIrisPreloader() {
      const preloader = document.getElementById('km-preloader');
      const countEl = document.getElementById('km-count');
      const fillEl = document.getElementById('km-fill');
      const statusEl = document.getElementById('km-status');
      const whitePage = document.getElementById('white-page');
      const greetingEl = document.querySelector('.greeting');
    
      if (!preloader || !countEl || !fillEl || !statusEl || !whitePage || !greetingEl) return;
    
      // Duration: 4.5s
      const DURATION = 4500;
    
      let currentProgress = 0;
      let targetProgress = 0;
      let startTime = performance.now();
      let lastFrameTime = performance.now();
      let animationId = null;
      let isRunning = false;
    
      // Frame loop with sub-pixel lerp
      function tick(now) {
        if (!isRunning) return;
    
        const dt = Math.min((now - lastFrameTime) / 1000, 0.1);
        lastFrameTime = now;
    
        const elapsed = now - startTime;
        const t = Math.min(elapsed / DURATION, 1);
    
        const easedT = t < 0.5
          ? 4 * t * t * t
          : 1 - Math.pow(-2 * t + 2, 3) / 2;
    
        targetProgress = easedT * 100;
    
        const lerpSpeed = 7.5;
        const lerpFactor = 1 - Math.exp(-lerpSpeed * dt);
        currentProgress += (targetProgress - currentProgress) * lerpFactor;
    
        if (t >= 1 && (100 - currentProgress) < 0.25) {
          currentProgress = 100;
          updateDisplay(100);
          finish();
          return;
        }
    
        updateDisplay(currentProgress);
        animationId = requestAnimationFrame(tick);
      }
    
      function updateDisplay(val) {
        const intVal = Math.min(Math.floor(val), 100);
    
        countEl.textContent = intVal;
        fillEl.style.width = `${val.toFixed(2)}%`;
    
        if (intVal < 25) {
          statusEl.textContent = 'INITIALIZING';
        } else if (intVal < 60) {
          statusEl.textContent = 'LOADING';
        } else if (intVal < 90) {
          statusEl.textContent = 'SYNCING';
        } else if (intVal < 100) {
          statusEl.textContent = 'FINALIZING';
        } else {
          statusEl.textContent = 'READY';
        }
      }
    
      function finish() {
        isRunning = false;
        preloader.classList.add('km-completing');
    
        setTimeout(() => {
          whitePage.classList.add('reveal');
        }, 200);
    
        setTimeout(() => {
          greetingEl.classList.add('fade-in');
          preloader.style.display = 'none';
        }, 1250);
      }
    
      function start() {
        isRunning = true;
        startTime = performance.now();
        lastFrameTime = performance.now();
        currentProgress = 0;
        targetProgress = 0;
    
        preloader.style.display = 'flex';
        preloader.classList.remove('km-completing');
        whitePage.classList.remove('reveal');
        greetingEl.classList.remove('fade-in');
        updateDisplay(0);
    
        if (animationId) cancelAnimationFrame(animationId);
        animationId = requestAnimationFrame(tick);
      }
    
      start();
    
      // Dev shortcut: Space or R to replay
      window.addEventListener('keydown', (e) => {
        if (e.code === 'Space' || e.key.toLowerCase() === 'r') {
          e.preventDefault();
          start();
        }
      });
    })();
    


    This project demonstrates how HTML, CSS, and JavaScript can work together to create a visually engaging loading experience. By combining CSS animations, JavaScript-controlled progress, and smooth transitions, we built a modern kinetic morph loader that makes waiting feel more interactive and enjoyable.

    If your project has problems, don’t worry. Just click to download the source code and face your coding challenges with excitement. Have fun coding!

    Animation magic magic navigation menu menu naviagtion Web Design
    Share. Copy Link Twitter Facebook LinkedIn Email WhatsApp
    Previous ArticleHow to make Love Letter Animation using HTML CSS & JavaScript
    Coding Stella
    • Website

    Related Posts

    JavaScript

    How to make Love Letter Animation using HTML CSS & JavaScript

    29 October 2026
    JavaScript

    Navigation Menu Tab Vol. 1 using HTML, CSS & JavaScript

    24 October 2026
    HTML & CSS

    How to make Interactive Spider Cursor Animation using HTML CSS JS

    22 October 2026
    Add A Comment
    Leave A Reply Cancel Reply

    Trending Post

    Master Frontend in 100 Days Ebook

    2 March 202432K Views

    How to make Modern Login Form using HTML & CSS | Glassmorphism

    11 January 202431K Views

    How to make I love you Animation in HTML CSS & JavaScript

    14 February 202424K Views

    How to make Valentine’s Day Card using HTML & CSS

    13 February 202415K Views
    Follow Us
    • Instagram
    • Facebook
    • YouTube
    • Twitter
    Featured Post

    How to make QR Code Generator using HTML CSS & JavaScript

    14 January 2024

    Attributes in HTML: List, Tag Attributes, Elements, and More!

    30 January 2024

    Best Programming Languages for Web Development

    25 January 2024

    How to create Newton’s Sun and Moon Loading Animation using HTML CSS and JS

    5 July 2025

    How to make Kinetic Morph Loader using HTML CSS & JavaScript

    1 November 2026

    How to make Love Letter Animation using HTML CSS & JavaScript

    29 October 2026

    Navigation Menu Tab Vol. 1 using HTML, CSS & JavaScript

    24 October 2026

    How to make Interactive Spider Cursor Animation using HTML CSS JS

    22 October 2026
    Facebook X (Twitter) Instagram YouTube
    • About Us
    • Privacy Policy
    • Return and Refund Policy
    • Terms and Conditions
    • Contact Us
    • Buy me a coffee
    © 2026 Coding Stella. Made with 💙 by @coding.stella

    Type above and press Enter to search. Press Esc to cancel.