ToolsWaves
Free GSAP Landing Page Template ยท ToolsWaves ยท AI & SaaS

Free AI Voice SaaS Hero Template with Sonar-Pulse Loader

Free AI SaaS landing page template from ToolsWaves featuring a pulsing-orb loader with sonar rings and voice bars, a glass 3D scene with floating orbs and checkmark, a custom cursor, ambient cursor glow, and particle burst on CTA. Free with no sign-up.

AISaaSOrbGlassmorphicCustom Cursor

About this ToolsWaves template

This free AI SaaS landing page template on ToolsWaves is built for voice-first AI products โ€” automated calling platforms, AI sales agents, voice analytics tools, or conversational AI services. The loader uses a pulsing orb with sonar rings and animated voice bars to signal the audio-first nature of the product before any text loads. The hero scene uses glassmorphic 3D effects with floating orbs and a verification checkmark, while a custom cursor and ambient glow that follows mouse movement add a sense of premium interactivity.

Use this template for B2B AI startups, voice technology companies, or any SaaS brand that wants to convey both technical sophistication and human warmth. ToolsWaves provides this template free for any commercial or personal project. Copy the HTML, CSS, and JS files, replace the demo branding and product references, and adjust the orb colors via CSS variables to match your visual identity. The GSAP particle effects can be tuned or disabled depending on the energy level you want for your brand.

Copy the code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Free AI Voice SaaS Hero Template with Sonar-Pulse Loader</title>
  <meta name="description" content="Free GSAP AI SaaS landing page hero from ToolsWaves" />
  <meta name="generator" content="ToolsWaves - https://toolswaves.in/landing-pages" />

  <!-- Open Graph -->
  <meta property="og:title" content="Free AI Voice SaaS Hero Template with Sonar-Pulse Loader" />
  <meta property="og:description" content="Free GSAP AI SaaS landing page hero from ToolsWaves" />
  <meta property="og:type" content="website" />
  <meta property="og:image" content="https://toolswaves.in/og?title=Free%20AI%20Voice%20SaaS%20Hero%20Template%20with%20Sonar-Pulse%20Loader&category=Landing%20Page&icon=%F0%9F%93%84" />

  <!-- Twitter -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="Free AI Voice SaaS Hero Template with Sonar-Pulse Loader" />
  <meta name="twitter:description" content="Free GSAP AI SaaS landing page hero from ToolsWaves" />
  <meta name="twitter:image" content="https://toolswaves.in/og?title=Free%20AI%20Voice%20SaaS%20Hero%20Template%20with%20Sonar-Pulse%20Loader&category=Landing%20Page&icon=%F0%9F%93%84" />

  <!-- Bootstrap (used by template โ€” replace with your own framework if you prefer) -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" />

  <style>
* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
    --bg:    #0a0418;
    --bg-2:  #050210;
    --ink:   #ffffff;
    --muted: rgba(255, 255, 255, 0.6);
    --line:  rgba(255, 255, 255, 0.1);
    --purple: #8b2fe6;
    --purple-deep: #6c1dc4;
    --purple-soft: #c084fc;
    --lavender: #e9d5ff;
    --glass-pink: #ec4cff;
    --chip-bg: rgba(139, 47, 230, 0.08);
    --chip-border: rgba(139, 47, 230, 0.25);
}

html, body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    background: var(--bg);
    color: var(--ink);
    overflow: hidden;
    cursor: none;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
@media (hover: none), (pointer: coarse) { html, body { cursor: auto; } }

.back-link {
    position: fixed;
    bottom: 0.75rem;
    left: 0.75rem;
    z-index: 1000;
    color: var(--ink);
    text-decoration: none;
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    padding: 0.45rem 0.9rem;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 999px;
    backdrop-filter: blur(8px);
    cursor: none;
    opacity: 0.85;
    transition: opacity 0.3s, transform 0.3s;
}
.back-link:hover { opacity: 1; transform: translateY(-2px); }

/* =========================================================
   CUSTOM CURSOR
   ========================================================= */
.cursor-dot, .cursor-ring {
    position: fixed;
    top: 0; left: 0;
    pointer-events: none;
    z-index: 10000;
    transform: translate(-50%, -50%);
    will-change: transform;
}
.cursor-dot {
    width: 6px; height: 6px;
    background: var(--purple-soft);
    border-radius: 50%;
}
.cursor-ring {
    width: 34px; height: 34px;
    border: 1px solid rgba(192, 132, 252, 0.5);
    border-radius: 50%;
    transition: width 0.3s, height 0.3s, border-color 0.3s, background 0.3s;
}
.cursor-ring.is-hover {
    width: 60px; height: 60px;
    border-color: var(--purple-soft);
    background: rgba(192, 132, 252, 0.08);
}
@media (hover: none), (pointer: coarse) { .cursor-dot, .cursor-ring { display: none; } }

/* =========================================================
   LOADER โ€” Pulsing orb + voice waves
   ========================================================= */
.loader {
    position: fixed;
    inset: 0;
    z-index: 999;
    background: radial-gradient(ellipse at center, var(--bg) 0%, var(--bg-2) 100%);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3rem;
}

.loader__stage {
    position: relative;
    width: 260px;
    height: 260px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.loader__rings {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}
.loader__rings span {
    position: absolute;
    width: 80px; height: 80px;
    border-radius: 50%;
    border: 2px solid rgba(192, 132, 252, 0.5);
    will-change: transform, opacity;
}

.loader__orb {
    position: relative;
    width: 90px; height: 90px;
    border-radius: 50%;
    background: radial-gradient(circle at 30% 30%, var(--purple-soft), var(--purple-deep) 70%);
    box-shadow: 0 0 40px rgba(139, 47, 230, 0.6),
                inset 0 0 20px rgba(233, 213, 255, 0.3);
    z-index: 2;
    will-change: transform;
    display: flex;
    align-items: center;
    justify-content: center;
}
.loader__orb-inner {
    position: absolute;
    inset: 15%;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 35%, rgba(255, 255, 255, 0.4), transparent 60%);
}
.loader__orb-core {
    position: absolute;
    inset: 35%;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.6);
    filter: blur(4px);
    will-change: transform;
}
.loader__check {
    position: absolute;
    width: 40px;
    height: 40px;
    z-index: 3;
    opacity: 0;
}

.loader__bars {
    position: absolute;
    bottom: -30px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 3px;
    align-items: center;
    height: 40px;
}
.loader__bars span {
    display: block;
    width: 3px;
    min-height: 4px;
    background: var(--purple-soft);
    border-radius: 2px;
    will-change: transform, height;
    box-shadow: 0 0 6px rgba(192, 132, 252, 0.5);
}

.loader__meta {
    display: flex;
    gap: 1.25rem;
    align-items: center;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.75rem;
    letter-spacing: 0.3em;
    color: var(--muted);
    text-transform: uppercase;
}
.loader__counter {
    color: var(--purple-soft);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    min-width: 3ch;
}

/* =========================================================
   VOXR SECTION
   ========================================================= */
.voxr {
    position: relative;
    width: 100%;
    height: 100vh;
    min-height: 780px;
    overflow: hidden;
    background:
        radial-gradient(ellipse at 75% 30%, rgba(139, 47, 230, 0.18) 0%, transparent 50%),
        radial-gradient(ellipse at 30% 70%, rgba(139, 47, 230, 0.08) 0%, transparent 60%),
        linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%);
}

.voxr__glow {
    position: absolute;
    top: 50%; left: 50%;
    width: 500px; height: 500px;
    transform: translate(-50%, -50%);
    background: radial-gradient(circle, rgba(139, 47, 230, 0.2) 0%, transparent 70%);
    filter: blur(40px);
    pointer-events: none;
    z-index: 1;
    will-change: transform;
}

/* ---------- Top nav ---------- */
.topnav {
    position: relative;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 2rem;
    padding: 1.5rem 2.5rem;
}

.topnav__logo {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    text-decoration: none;
    color: var(--ink);
    font-weight: 700;
    font-size: 1.4rem;
    letter-spacing: 0.02em;
    cursor: none;
    will-change: transform;
}
.topnav__logo-mark { width: 32px; height: 32px; }
.topnav__logo-mark svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 0 8px rgba(139, 47, 230, 0.5)); }
.topnav__logo-text em {
    font-style: normal;
    background: linear-gradient(135deg, var(--purple-soft), var(--purple));
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

.topnav__links {
    margin: 0 auto;
    display: flex;
    gap: 2.5rem;
}
.topnav__links a {
    position: relative;
    color: var(--ink);
    text-decoration: none;
    font-weight: 500;
    font-size: 1rem;
    padding: 0.3rem 0;
    cursor: none;
    will-change: transform;
}
.topnav__links a::after {
    content: '';
    position: absolute;
    left: 0; bottom: 0;
    width: 100%; height: 1.5px;
    background: var(--purple-soft);
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.4s cubic-bezier(0.65, 0, 0.35, 1);
    border-radius: 2px;
}
.topnav__links a:hover::after { transform: scaleX(1); transform-origin: left; }

.topnav__actions {
    display: flex;
    align-items: center;
    gap: 0;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 999px;
    padding: 0.3rem;
}
.topnav__contact {
    color: var(--ink);
    text-decoration: none;
    padding: 0.7rem 1.4rem;
    font-weight: 500;
    font-size: 0.95rem;
    cursor: none;
    will-change: transform;
    border-radius: 999px;
    transition: background 0.3s;
}
.topnav__contact:hover { background: rgba(255, 255, 255, 0.06); }

.topnav__login {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    background: #fff;
    color: var(--bg);
    text-decoration: none;
    padding: 0.5rem 0.5rem 0.5rem 1.25rem;
    border-radius: 999px;
    font-weight: 600;
    font-size: 0.95rem;
    cursor: none;
    will-change: transform;
    box-shadow: 0 0 30px rgba(139, 47, 230, 0.5);
    position: relative;
}
.topnav__login-arrow {
    width: 30px; height: 30px;
    background: var(--purple);
    color: #fff;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.3s;
}
.topnav__login-arrow svg { width: 14px; height: 14px; }
.topnav__login:hover .topnav__login-arrow { transform: rotate(-45deg); }

/* ---------- Hero ---------- */
.hero {
    position: relative;
    z-index: 10;
    display: grid;
    grid-template-columns: 1.2fr 1fr;
    gap: 3rem;
    padding: 2rem 3rem 3rem;
    align-items: flex-start;
    min-height: calc(100vh - 100px);
}

.hero__left { max-width: 720px; padding-top: 2rem; }

.hero__title {
    font-weight: 700;
    font-size: clamp(2.5rem, 5.5vw, 5rem);
    line-height: 1.1;
    letter-spacing: -0.02em;
    color: var(--ink);
    margin-bottom: 2rem;
}
.hero__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.04em;
}
.hero__line em {
    font-style: normal;
    background: linear-gradient(135deg, var(--lavender) 0%, #c9a7e9 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}
.hero__char {
    display: inline-block;
    will-change: transform;
}
.hero__char.is-space { width: 0.25em; }

.hero__desc {
    font-size: 1rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.6);
    margin-bottom: 2.5rem;
    max-width: 560px;
}

.cta-big {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    background: #fff;
    color: var(--bg);
    text-decoration: none;
    padding: 0.7rem 0.7rem 0.7rem 1.75rem;
    border-radius: 999px;
    font-weight: 600;
    font-size: 1rem;
    cursor: none;
    will-change: transform;
    box-shadow: 0 0 50px rgba(139, 47, 230, 0.6),
                0 0 20px rgba(139, 47, 230, 0.4);
}
.cta-big__arrow {
    width: 38px; height: 38px;
    background: var(--purple);
    color: #fff;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.3s;
}
.cta-big__arrow svg { width: 16px; height: 16px; }
.cta-big:hover .cta-big__arrow { transform: rotate(-45deg); }

/* ---------- Chips (right column) ---------- */
.hero__chips {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.75rem;
    padding-top: 2rem;
}
.chip {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.8rem 1.25rem;
    background: var(--chip-bg);
    border: 1px solid var(--chip-border);
    border-radius: 999px;
    backdrop-filter: blur(10px);
    font-size: 0.95rem;
    color: var(--ink);
    cursor: none;
    will-change: transform;
    transition: border-color 0.3s, background 0.3s;
}
.chip__icon {
    width: 18px; height: 18px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.chip__icon svg { width: 100%; height: 100%; }
.chip:hover {
    border-color: var(--purple-soft);
    background: rgba(139, 47, 230, 0.15);
}

.chip:nth-child(1) { margin-left: 0; }
.chip:nth-child(2) { margin-left: 12%; }
.chip:nth-child(3) { margin-left: 4%; }

/* ---------- 3D Scene ---------- */
.scene {
    position: absolute;
    left: 45%;
    right: 0;
    bottom: 0;
    height: 45%;
    z-index: 5;
    pointer-events: none;
    perspective: 1000px;
    overflow: hidden;
}

.scene__platform {
    position: absolute;
    left: 5%;
    right: -10%;
    bottom: 0;
    height: 50%;
    background: radial-gradient(ellipse at center top, rgba(236, 76, 255, 0.3) 0%, rgba(139, 47, 230, 0.15) 50%, transparent 100%);
    border-radius: 100% 100% 0 0 / 60% 60% 0 0;
    filter: blur(2px);
    will-change: transform;
}
.scene__light {
    position: absolute;
    left: 30%;
    right: 20%;
    bottom: 10%;
    height: 20%;
    background: linear-gradient(90deg, transparent, rgba(236, 76, 255, 0.6), transparent);
    filter: blur(8px);
    border-radius: 50%;
    will-change: transform;
}

.scene__check {
    position: absolute;
    left: 18%;
    bottom: 35%;
    width: 120px;
    height: 140px;
    transform-style: preserve-3d;
    will-change: transform;
    z-index: 3;
}
.scene__check-face {
    width: 100%; height: 100%;
    background: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.05) 100%);
    border: 1.5px solid rgba(236, 76, 255, 0.4);
    border-radius: 20px 20px 40px 40px / 20px 20px 50px 50px;
    backdrop-filter: blur(20px);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow:
        0 20px 40px rgba(139, 47, 230, 0.3),
        inset 0 2px 6px rgba(255, 255, 255, 0.2),
        0 0 30px rgba(236, 76, 255, 0.2);
}
.scene__check-face svg {
    width: 48px;
    height: 48px;
    filter: drop-shadow(0 2px 6px rgba(236, 76, 255, 0.6));
}

.scene__orb {
    position: absolute;
    border-radius: 50%;
    background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.4), rgba(233, 213, 255, 0.1) 50%, rgba(139, 47, 230, 0.05) 100%);
    backdrop-filter: blur(6px);
    border: 1px solid rgba(255, 255, 255, 0.1);
    box-shadow: 0 10px 30px rgba(139, 47, 230, 0.25),
                inset 0 3px 10px rgba(255, 255, 255, 0.2);
    will-change: transform;
}
.scene__orb--1 { width: 70px; height: 70px; bottom: 58%; left: 42%; }
.scene__orb--2 { width: 60px; height: 60px; bottom: 48%; left: 60%; }
.scene__orb--3 { width: 50px; height: 50px; bottom: 65%; left: 78%; }
.scene__orb--4 { width: 44px; height: 44px; bottom: 40%; left: 88%; }
.scene__orb--5 { width: 38px; height: 38px; bottom: 70%; left: 92%; }

/* ---------- Initial hidden states ---------- */
[data-magnetic], [data-link], [data-fade], [data-title-line], [data-chip], [data-cta], [data-scene] {
    opacity: 0;
}

/* ---------- Responsive ---------- */
@media (max-width: 1100px) {
    .topnav__links { display: none; }
    .hero { grid-template-columns: 1fr; gap: 2rem; padding: 2rem 1.5rem; }
    .hero__chips { flex-direction: row; flex-wrap: wrap; padding-top: 0; }
    .chip { margin-left: 0 !important; font-size: 0.8rem; padding: 0.6rem 1rem; }
    .scene { left: 50%; height: 40%; }
    .scene__check { width: 80px; height: 95px; }
}
@media (max-width: 760px) {
    .topnav { padding: 1rem 1.25rem; gap: 0.5rem; }
    .topnav__logo-text { font-size: 1.1rem; }
    .topnav__contact { display: none; }
    .hero { padding: 1rem 1.25rem; gap: 1.5rem; }
    .hero__title { font-size: clamp(1.75rem, 9vw, 3rem); }
    .hero__desc { font-size: 0.9rem; }
    .cta-big { font-size: 0.9rem; padding: 0.55rem 0.55rem 0.55rem 1.4rem; }
    .cta-big__arrow { width: 32px; height: 32px; }
    .scene { height: 30%; left: 40%; }
    .scene__check { display: none; }
    .scene__orb { transform: scale(0.7); }
}
@media (max-width: 480px) {
    .hero__chips { display: none; }
}

  </style>
</head>
<body>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Banner โ€” Voxr AI</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@500;600&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="banner-voxr-ai.css">
</head>
<body>
    

    <!-- Custom cursor -->
    <div class="cursor-dot" id="cursor-dot"></div>
    <div class="cursor-ring" id="cursor-ring"></div>

    <!-- ========== LOADER โ€” Pulsing AI orb ========== -->
    <div class="loader" id="loader">
        <div class="loader__stage">
            <div class="loader__rings" id="loader-rings"></div>
            <div class="loader__orb" id="loader-orb">
                <div class="loader__orb-inner"></div>
                <div class="loader__orb-core"></div>
                <svg class="loader__check" id="loader-check" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3">
                    <path d="M5 12l5 5 9-11" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
            </div>
            <div class="loader__bars" id="loader-bars"></div>
        </div>
        <div class="loader__meta">
            <span class="loader__label" id="loader-label">INITIALIZING AI</span>
            <span class="loader__counter" id="loader-counter">0%</span>
        </div>
    </div>

    <section class="voxr" id="voxr">

        <!-- Ambient radial glow that follows cursor -->
        <div class="voxr__glow" id="glow"></div>

        <!-- ========== TOP NAV ========== -->
        <header class="topnav">
            <a href="#" class="topnav__logo" data-magnetic>
                <span class="topnav__logo-mark" id="logo-mark">
                    <svg viewBox="0 0 40 40" aria-hidden="true">
                        <path d="M6 6 L 20 30 L 34 6" stroke="url(#logoGrad)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
                        <circle cx="32" cy="10" r="2.5" fill="#c084fc"/>
                        <defs>
                            <linearGradient id="logoGrad" x1="0" x2="1" y1="0" y2="1">
                                <stop offset="0%" stop-color="#e9d5ff"/>
                                <stop offset="100%" stop-color="#8b2fe6"/>
                            </linearGradient>
                        </defs>
                    </svg>
                </span>
                <span class="topnav__logo-text">VOXR <em>AI</em></span>
            </a>
            <nav class="topnav__links">
                <a href="#" data-magnetic data-link>Features</a>
                <a href="#" data-magnetic data-link>Pricing</a>
                <a href="#" data-magnetic data-link>About Us</a>
            </nav>
            <div class="topnav__actions">
                <a href="#" class="topnav__contact" data-magnetic>Contact us</a>
                <a href="#" class="topnav__login" data-magnetic>
                    <span>Login</span>
                    <span class="topnav__login-arrow">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                            <path d="M5 12h14M13 6l6 6-6 6" stroke-linecap="round" stroke-linejoin="round"/>
                        </svg>
                    </span>
                </a>
            </div>
        </header>

        <!-- ========== HERO ========== -->
        <div class="hero" id="hero">
            <!-- Left column -->
            <div class="hero__left">
                <h1 class="hero__title" aria-label="Stop Chasing Leads. Start Closing Them.">
                    <span class="hero__line" data-title-line>Stop Chasing</span>
                    <span class="hero__line" data-title-line><em>Leads.</em> Start</span>
                    <span class="hero__line" data-title-line><em>Closing Them.</em></span>
                </h1>

                <p class="hero__desc" data-fade>
                    AI-powered outbound calling that works leads at scale, qualifies
                    intent in real-time, and either transfers hot prospects to your
                    closers instantly or books meetings directly on their calendar.
                </p>

                <a href="#" class="cta-big" data-magnetic data-cta>
                    <span class="cta-big__label">Try it now</span>
                    <span class="cta-big__arrow">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                            <path d="M5 12h14M13 6l6 6-6 6" stroke-linecap="round" stroke-linejoin="round"/>
                        </svg>
                    </span>
                </a>
            </div>

            <!-- Right column: feature chips -->
            <div class="hero__chips">
                <span class="chip" data-chip>
                    <span class="chip__icon">
                        <svg viewBox="0 0 24 24" fill="#c084fc"><path d="M12 2l2.4 7.6L22 12l-7.6 2.4L12 22l-2.4-7.6L2 12l7.6-2.4z"/></svg>
                    </span>
                    <span>Your smart scheduling assistant</span>
                </span>
                <span class="chip" data-chip>
                    <span class="chip__icon">
                        <svg viewBox="0 0 24 24" fill="none" stroke="#c084fc" stroke-width="3">
                            <path d="M4 12l5 5 11-13" stroke-linecap="round" stroke-linejoin="round"/>
                        </svg>
                    </span>
                    <span>Qualified appointment only</span>
                </span>
                <span class="chip" data-chip>
                    <span class="chip__icon">
                        <svg viewBox="0 0 24 24" fill="#c084fc">
                            <path d="M5 13l3-8 5 3 2-5 4 7-3 6-5-2-2 5z"/>
                        </svg>
                    </span>
                    <span>Increases efficiency by 80%</span>
                </span>
            </div>
        </div>

        <!-- ========== 3D SCENE (bottom) ========== -->
        <div class="scene" id="scene">
            <div class="scene__platform" data-scene></div>
            <div class="scene__light" data-scene></div>
            <div class="scene__check" data-scene>
                <div class="scene__check-face">
                    <svg viewBox="0 0 48 48" fill="none" stroke="#ec4cff" stroke-width="4">
                        <path d="M10 24l10 10 18-22" stroke-linecap="round" stroke-linejoin="round"/>
                    </svg>
                </div>
            </div>
            <div class="scene__orb scene__orb--1" data-scene></div>
            <div class="scene__orb scene__orb--2" data-scene></div>
            <div class="scene__orb scene__orb--3" data-scene></div>
            <div class="scene__orb scene__orb--4" data-scene></div>
            <div class="scene__orb scene__orb--5" data-scene></div>
        </div>
    </section>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
    <script src="banner-voxr-ai.js"></script>
</body>
</html>

</body>
</html>
JSbanner-voxr-ai.jsโ€” GSAP animation timeline
(() => {
    const root = document.getElementById('voxr');
    if (!root) return;

    // -------------------------------------------------------------------
    //  SPLIT TITLE (wrap em/text nodes into char spans, preserving gradient)
    // -------------------------------------------------------------------
    const titleLines = root.querySelectorAll('[data-title-line]');
    titleLines.forEach((line) => {
        // Walk children and wrap text nodes' characters without losing em styling
        const frag = document.createDocumentFragment();
        line.childNodes.forEach((node) => {
            if (node.nodeType === Node.TEXT_NODE) {
                [...node.textContent].forEach((ch) => {
                    const span = document.createElement('span');
                    span.className = 'hero__char';
                    if (ch === ' ') { span.classList.add('is-space'); span.innerHTML = '&nbsp;'; }
                    else span.textContent = ch;
                    frag.appendChild(span);
                });
            } else if (node.nodeType === Node.ELEMENT_NODE) {
                // em element โ€” wrap its chars too but keep the em wrapper
                const emText = node.textContent;
                node.textContent = '';
                [...emText].forEach((ch) => {
                    const span = document.createElement('span');
                    span.className = 'hero__char';
                    if (ch === ' ') { span.classList.add('is-space'); span.innerHTML = '&nbsp;'; }
                    else span.textContent = ch;
                    node.appendChild(span);
                });
                frag.appendChild(node);
            }
        });
        line.textContent = '';
        line.appendChild(frag);
    });

    // -------------------------------------------------------------------
    //  BUILD LOADER RINGS + BARS
    // -------------------------------------------------------------------
    const ringsHost = document.getElementById('loader-rings');
    const RING_COUNT = 4;
    for (let i = 0; i < RING_COUNT; i++) ringsHost.appendChild(document.createElement('span'));
    const ringEls = ringsHost.querySelectorAll('span');

    const barsHost = document.getElementById('loader-bars');
    const BAR_COUNT = 16;
    for (let i = 0; i < BAR_COUNT; i++) barsHost.appendChild(document.createElement('span'));
    const barEls = barsHost.querySelectorAll('span');

    // -------------------------------------------------------------------
    //  REFERENCES
    // -------------------------------------------------------------------
    const loader = document.getElementById('loader');
    const loaderOrb = document.getElementById('loader-orb');
    const loaderCheck = document.getElementById('loader-check');
    const loaderCounter = document.getElementById('loader-counter');
    const loaderLabel = document.getElementById('loader-label');

    const cursorDot = document.getElementById('cursor-dot');
    const cursorRing = document.getElementById('cursor-ring');

    const glow = document.getElementById('glow');
    const magnetics = root.querySelectorAll('[data-magnetic]');
    const titleChars = root.querySelectorAll('.hero__char');
    const lines = root.querySelectorAll('[data-title-line]');
    const fades = root.querySelectorAll('[data-fade]');
    const chips = root.querySelectorAll('[data-chip]');
    const cta = root.querySelector('[data-cta]');
    const sceneEls = root.querySelectorAll('[data-scene]');
    const logoMark = document.getElementById('logo-mark');

    // -------------------------------------------------------------------
    //  INITIAL STATES
    // -------------------------------------------------------------------
    gsap.set(magnetics, { y: -15, opacity: 0 });
    gsap.set(titleChars, { yPercent: 110, opacity: 0 });
    gsap.set(lines, { opacity: 1 });
    gsap.set(fades, { y: 20, opacity: 0 });
    gsap.set(chips, { x: 40, opacity: 0 });
    gsap.set(cta, { y: 30, opacity: 0, scale: 0.9 });
    gsap.set(sceneEls, { opacity: 0 });
    gsap.set(ringEls, { scale: 0.6, opacity: 0 });

    // -------------------------------------------------------------------
    //  LOADER TIMELINE
    // -------------------------------------------------------------------
    const loaderTl = gsap.timeline({ onComplete: playScene });

    // Rings emanate continuously
    const ringTweens = [];
    ringEls.forEach((r, i) => {
        const t = gsap.fromTo(r,
            { scale: 0.8, opacity: 0.7 },
            {
                scale: 2.8,
                opacity: 0,
                duration: 2,
                repeat: -1,
                ease: 'power1.out',
                delay: i * 0.5,
            }
        );
        ringTweens.push(t);
    });

    // Orb pulses
    gsap.to(loaderOrb, {
        scale: 1.1,
        duration: 0.8,
        yoyo: true,
        repeat: -1,
        ease: 'sine.inOut',
    });

    // Voice bars animate heights
    barEls.forEach((bar, i) => {
        gsap.to(bar, {
            height: () => gsap.utils.random(8, 38),
            duration: () => gsap.utils.random(0.25, 0.5),
            yoyo: true,
            repeat: -1,
            ease: 'sine.inOut',
            delay: i * 0.04,
        });
    });

    // Counter + label rotation
    const p = { v: 0 };
    loaderTl.to(p, {
        v: 100,
        duration: 2.4,
        ease: 'power1.inOut',
        onUpdate: () => {
            loaderCounter.textContent = Math.floor(p.v) + '%';
            if (p.v > 25 && loaderLabel.textContent === 'INITIALIZING AI') loaderLabel.textContent = 'CONNECTING VOICE';
            if (p.v > 55 && loaderLabel.textContent === 'CONNECTING VOICE') loaderLabel.textContent = 'TRAINING MODEL';
            if (p.v > 90 && loaderLabel.textContent === 'TRAINING MODEL') loaderLabel.textContent = 'READY';
        },
    });

    // Checkmark appears at end
    loaderTl.to(loaderCheck, {
        opacity: 1,
        duration: 0.3,
        ease: 'back.out(2)',
    }, '+=0.1');
    loaderTl.from(loaderCheck, {
        scale: 0,
        rotation: -45,
        duration: 0.5,
        ease: 'back.out(2.5)',
    }, '<');

    // Exit: orb expands, rings burst out, loader fades
    loaderTl.to(ringEls, {
        scale: 6,
        opacity: 0,
        duration: 0.8,
        ease: 'power2.out',
        onStart: () => ringTweens.forEach(t => t.pause()),
    }, '+=0.3');
    loaderTl.to(loaderOrb, {
        scale: 2.5,
        opacity: 0,
        duration: 0.7,
        ease: 'power3.in',
    }, '-=0.5');
    loaderTl.to([loaderCounter, loaderLabel, barsHost], {
        y: 10, opacity: 0, duration: 0.3, stagger: 0.04,
    }, '-=0.6');
    loaderTl.to(loader, {
        opacity: 0, duration: 0.4, ease: 'power2.inOut',
    }, '-=0.2');
    loaderTl.set(loader, { display: 'none' });

    // -------------------------------------------------------------------
    //  MAIN SCENE ENTRANCE
    // -------------------------------------------------------------------
    function playScene() {
        const tl = gsap.timeline({ defaults: { ease: 'power3.out' } });

        // Nav
        tl.to(magnetics, {
            y: 0, opacity: 1,
            duration: 0.6,
            stagger: 0.05,
        }, 0);

        // Title chars
        tl.to(titleChars, {
            yPercent: 0, opacity: 1,
            duration: 1.1,
            stagger: 0.018,
            ease: 'expo.out',
        }, 0.3);

        // Desc + CTA
        tl.to(fades, {
            y: 0, opacity: 1,
            duration: 0.7,
            stagger: 0.15,
        }, 0.9);
        tl.to(cta, {
            y: 0, opacity: 1, scale: 1,
            duration: 0.8,
            ease: 'back.out(1.6)',
        }, 1.1);

        // Chips stagger in from right
        tl.to(chips, {
            x: 0, opacity: 1,
            duration: 0.7,
            stagger: 0.1,
            ease: 'power4.out',
        }, 0.8);

        // 3D scene fades in
        tl.to(sceneEls, {
            opacity: 1,
            duration: 1.2,
            stagger: 0.08,
            ease: 'power2.out',
        }, 1);

        tl.call(startContinuous, null, 1.8);
        tl.call(enableInteractions, null, 1.8);
    }

    // -------------------------------------------------------------------
    //  CONTINUOUS
    // -------------------------------------------------------------------
    function startContinuous() {
        // Orbs float at different rates
        root.querySelectorAll('.scene__orb').forEach((orb, i) => {
            gsap.to(orb, {
                y: `-=${12 + i * 3}`,
                duration: 2 + i * 0.4,
                yoyo: true,
                repeat: -1,
                ease: 'sine.inOut',
                delay: i * 0.15,
            });
        });

        // Check icon floats + rotates slightly
        const check = root.querySelector('.scene__check');
        gsap.to(check, {
            y: '-=15',
            rotation: 3,
            duration: 3,
            yoyo: true,
            repeat: -1,
            ease: 'sine.inOut',
        });

        // Chips subtle float
        chips.forEach((chip, i) => {
            gsap.to(chip, {
                y: '-=6',
                duration: 2.2 + i * 0.3,
                yoyo: true,
                repeat: -1,
                ease: 'sine.inOut',
                delay: i * 0.2,
            });
        });

        // Light sweep under checkmark
        const light = root.querySelector('.scene__light');
        gsap.to(light, {
            x: 60, opacity: 0.6,
            duration: 3,
            yoyo: true,
            repeat: -1,
            ease: 'sine.inOut',
        });
    }

    // -------------------------------------------------------------------
    //  INTERACTIONS
    // -------------------------------------------------------------------
    function enableInteractions() {

        // ---- Custom cursor ----
        let mx = window.innerWidth / 2, my = window.innerHeight / 2;
        let rx = mx, ry = my;
        window.addEventListener('mousemove', (e) => { mx = e.clientX; my = e.clientY; });
        gsap.ticker.add(() => {
            rx += (mx - rx) * 0.18;
            ry += (my - ry) * 0.18;
            gsap.set(cursorDot, { x: mx, y: my });
            gsap.set(cursorRing, { x: rx, y: ry });
        });

        const hovers = root.querySelectorAll('a, button, [data-magnetic], [data-chip], .scene__orb, .hero__char');
        hovers.forEach((el) => {
            el.addEventListener('mouseenter', () => cursorRing.classList.add('is-hover'));
            el.addEventListener('mouseleave', () => cursorRing.classList.remove('is-hover'));
        });

        // ---- Ambient glow follows cursor ----
        let gx = 0, gy = 0, gcx = 0, gcy = 0;
        root.addEventListener('mousemove', (e) => {
            const r = root.getBoundingClientRect();
            gx = e.clientX - r.left;
            gy = e.clientY - r.top;
        });
        gsap.ticker.add(() => {
            gcx += (gx - gcx) * 0.04;
            gcy += (gy - gcy) * 0.04;
            gsap.set(glow, { x: gcx - window.innerWidth / 2, y: gcy - window.innerHeight / 2 });
        });

        // ---- Magnetic ----
        magnetics.forEach((el) => {
            const strength = el.classList.contains('cta-big') ? 0.3
                : el.classList.contains('topnav__login') ? 0.3
                : el.classList.contains('topnav__logo') ? 0.2
                : 0.22;
            el.addEventListener('mousemove', (e) => {
                const r = el.getBoundingClientRect();
                const cx = r.left + r.width / 2;
                const cy = r.top + r.height / 2;
                gsap.to(el, {
                    x: (e.clientX - cx) * strength,
                    y: (e.clientY - cy) * strength,
                    duration: 0.4, ease: 'power3.out',
                });
            });
            el.addEventListener('mouseleave', () => {
                gsap.to(el, { x: 0, y: 0, duration: 0.6, ease: 'elastic.out(1, 0.4)' });
            });
        });

        // ---- Logo: V mark draws on hover ----
        const logo = root.querySelector('.topnav__logo');
        logo.addEventListener('mouseenter', () => {
            gsap.fromTo(logoMark.querySelector('path'),
                { strokeDasharray: 80, strokeDashoffset: 80 },
                { strokeDashoffset: 0, duration: 0.6, ease: 'power2.out' }
            );
        });

        // ---- Title char proximity ----
        const hero = document.getElementById('hero');
        let tmx = -9999, tmy = -9999;
        hero.addEventListener('mousemove', (e) => { tmx = e.clientX; tmy = e.clientY; });
        hero.addEventListener('mouseleave', () => {
            tmx = -9999; tmy = -9999;
            titleChars.forEach((c) => gsap.to(c, { y: 0, duration: 0.5, ease: 'power3.out' }));
        });
        gsap.ticker.add(() => {
            if (tmx < 0) return;
            titleChars.forEach((c) => {
                const r = c.getBoundingClientRect();
                if (r.width === 0) return;
                const cx = r.left + r.width / 2;
                const cy = r.top + r.height / 2;
                const dx = tmx - cx, dy = tmy - cy;
                const dist = Math.sqrt(dx * dx + dy * dy);
                if (dist < 150) gsap.set(c, { y: -(1 - dist / 150) * 16 });
                else gsap.set(c, { y: 0 });
            });
        });

        // ---- Chips: hover pop + shimmer ----
        chips.forEach((chip) => {
            chip.addEventListener('mouseenter', () => {
                gsap.to(chip, {
                    scale: 1.04,
                    boxShadow: '0 10px 30px rgba(139, 47, 230, 0.35)',
                    duration: 0.3,
                    ease: 'back.out(2)',
                });
                // Icon wiggle
                const icon = chip.querySelector('.chip__icon');
                gsap.fromTo(icon,
                    { rotation: -15 },
                    { rotation: 15, duration: 0.1, yoyo: true, repeat: 3, ease: 'sine.inOut',
                      onComplete: () => gsap.to(icon, { rotation: 0, duration: 0.3 })
                    }
                );
            });
            chip.addEventListener('mouseleave', () => {
                gsap.to(chip, {
                    scale: 1, boxShadow: 'none',
                    duration: 0.4, ease: 'elastic.out(1, 0.4)',
                });
            });
        });

        // ---- CTA shimmer + click pulse ----
        cta.addEventListener('click', (e) => {
            e.preventDefault();
            gsap.fromTo(cta,
                { scale: 1 },
                { scale: 0.95, duration: 0.1, yoyo: true, repeat: 1, ease: 'sine.inOut' }
            );
            // Burst dots
            const rect = cta.getBoundingClientRect();
            const cx = rect.left + rect.width - 19;
            const cy = rect.top + rect.height / 2;
            for (let i = 0; i < 6; i++) {
                const dot = document.createElement('span');
                dot.style.position = 'fixed';
                dot.style.left = cx + 'px';
                dot.style.top = cy + 'px';
                dot.style.width = '5px';
                dot.style.height = '5px';
                dot.style.background = '#c084fc';
                dot.style.borderRadius = '50%';
                dot.style.pointerEvents = 'none';
                dot.style.zIndex = '200';
                dot.style.transform = 'translate(-50%, -50%)';
                dot.style.boxShadow = '0 0 8px #c084fc';
                document.body.appendChild(dot);
                const a = (i / 6) * Math.PI * 2;
                gsap.fromTo(dot,
                    { x: 0, y: 0, opacity: 1 },
                    {
                        x: Math.cos(a) * 50,
                        y: Math.sin(a) * 50,
                        opacity: 0,
                        scale: 1.8,
                        duration: 0.6,
                        ease: 'power2.out',
                        onComplete: () => dot.remove(),
                    }
                );
            }
        });

        // ---- Scene orbs: mouse parallax + click scatter ----
        const scene = document.getElementById('scene');
        const orbs = root.querySelectorAll('.scene__orb');
        let sx = 0, sy = 0, scx = 0, scy = 0;
        root.addEventListener('mousemove', (e) => {
            const r = root.getBoundingClientRect();
            sx = ((e.clientX - r.left) / r.width - 0.5) * 2;
            sy = ((e.clientY - r.top) / r.height - 0.5) * 2;
        });
        root.addEventListener('mouseleave', () => { sx = 0; sy = 0; });

        gsap.ticker.add(() => {
            scx += (sx - scx) * 0.05;
            scy += (sy - scy) * 0.05;
            orbs.forEach((orb, i) => {
                const depth = 0.4 + i * 0.15;
                gsap.set(orb, {
                    x: scx * 25 * depth,
                    y: scy * 18 * depth,
                });
            });
            const check = root.querySelector('.scene__check');
            if (check) gsap.set(check, {
                rotationY: scx * 8,
                rotationX: -scy * 6,
                transformPerspective: 1000,
                transformOrigin: 'center',
            });
        });

        // ---- Orbs: click bounces them away ----
        orbs.forEach((orb) => {
            orb.style.pointerEvents = 'auto';
            orb.addEventListener('mouseenter', () => {
                gsap.to(orb, { scale: 1.15, duration: 0.3, ease: 'back.out(2)' });
            });
            orb.addEventListener('mouseleave', () => {
                gsap.to(orb, { scale: 1, duration: 0.4, ease: 'elastic.out(1, 0.4)' });
            });
            orb.addEventListener('click', () => {
                gsap.fromTo(orb,
                    { x: 0 },
                    { x: (Math.random() - 0.5) * 80, duration: 0.3, yoyo: true, repeat: 1, ease: 'power2.out' }
                );
            });
        });
    }
})();

More GSAP landing pages