DEV Community

Shahibur Rahman
Shahibur Rahman

Posted on

Building a 3D Interactive Badge with HTML, CSS, and JavaScript


Badges, awards, and celebratory graphics often need more than plain text. With modern web technologies, you can create 3D interactive effects that look polished, respond to user actions, and bring personality to your site. In this post, we’ll build a 3D animated coin-like badge using HTML, CSS, SVG, and JavaScript.


🎯 What We’re Building

  • A circular badge with a golden edge.
  • Curved text and stars around the border.
  • A raised center with animated numbers and labels.
  • Particle sparkles floating inside.
  • 3D tilt on mouse move.
  • Ripple effect on click.
  • Smooth animations and gradients.

Here’s the breakdown of how to achieve it step by step.


🏗️ Step 1: HTML Structure

We’ll start with a container and build layers inside it.

<div class="badge-container">
  <div class="coin-edge"></div>

  <svg class="badge-svg" viewBox="0 0 200 200">
    <!-- Outer border and gradient -->
    <circle cx="100" cy="100" r="95" class="outer-circle" />
    <circle cx="100" cy="100" r="85" class="inner-circle" />

    <!-- Text paths for curved text -->
    <defs>
      <path id="circle-top" d="M20,100a80,80 0 1,1 160,0a80,80 0 1,1 -160,0" />
      <path id="circle-bottom" d="M180,100a80,80 0 1,1 -160,0a80,80 0 1,1 160,0" />
    </defs>

    <text class="curved-text">
      <textPath href="#circle-top" startOffset="50%" text-anchor="middle">
        20 YEARS ORLANDO & SURROUNDING CITIES
      </textPath>
    </text>

    <text class="curved-stars">
      <textPath href="#circle-bottom" startOffset="50%" text-anchor="middle">
        ★★★★★
      </textPath>
    </text>
  </svg>

  <div class="inner-content-wrapper">
    <div class="content2">
      <span class="click-effect"></span>
      <div class="twenty">20</div>
      <div class="years">YEARS</div>
      <div class="location">ORLANDO & SURROUNDING CITIES</div>
    </div>
  </div>

  <div class="particles"></div>
</div>
Enter fullscreen mode Exit fullscreen mode

This gives us:

  • .coin-edge: background rim.
  • <svg>: for border circles + curved text.
  • .inner-content-wrapper: raised middle with text.
  • .particles: container for sparkle elements.

✨ Step 2: How the Curved Text Works

One of the key visual features is the curved text around the badge. This is accomplished using SVG <textPath> elements that attach text to a circular <path>.

1. Define the Paths

Inside <defs>, we define invisible circular paths:

<defs>
  <path id="circle-top" d="M20,100a80,80 0 1,1 160,0a80,80 0 1,1 -160,0" />
  <path id="circle-bottom" d="M180,100a80,80 0 1,1 -160,0a80,80 0 1,1 160,0" />
</defs>
Enter fullscreen mode Exit fullscreen mode

These paths describe two arcs:

  • circle-top: curves text along the top half.
  • circle-bottom: curves text along the bottom half.

2. Attach Text with <textPath>

<text class="curved-text">
  <textPath href="#circle-top" startOffset="50%" text-anchor="middle">
    20 YEARS ORLANDO & SURROUNDING CITIES
  </textPath>
</text>
Enter fullscreen mode Exit fullscreen mode
  • href="#circle-top": attaches the text to the top path.
  • startOffset="50%": centers the text horizontally.
  • text-anchor="middle": ensures text alignment is balanced.

Similarly, the stars at the bottom use the circle-bottom path.

3. Adjusting Length with JavaScript (Optional)

If you want the text to fit perfectly along the arc, you can use JavaScript to measure the path length and set textLength dynamically:

function fitTextToArc(textElement, pathId) {
  const path = document.querySelector(pathId);
  const pathLength = path.getTotalLength();
  textElement.setAttribute('textLength', pathLength);
  textElement.setAttribute('lengthAdjust', 'spacing');
}

fitTextToArc(document.querySelector('.curved-text textPath'), '#circle-top');
fitTextToArc(document.querySelector('.curved-stars textPath'), '#circle-bottom');
Enter fullscreen mode Exit fullscreen mode

This ensures the text stretches evenly across the arc.


🎨 Step 3: CSS Styling & Animations

Core Layout

.badge-container {
  position: relative;
  width: 300px;
  height: 300px;
  perspective: 1000px;
  animation: badgePulse 4s infinite;
}

.coin-edge {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle, #ffcc33, #b8860b);
  box-shadow: inset 0 0 20px rgba(0,0,0,0.4);
}

.badge-svg {
  position: relative;
  width: 100%;
  height: 100%;
  z-index: 2;
}

.outer-circle {
  fill: none;
  stroke: #ffd700;
  stroke-width: 6;
}

.inner-circle {
  fill: url(#badgeGradient);
}
Enter fullscreen mode Exit fullscreen mode

Center Content

.inner-content-wrapper {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 60%;
  height: 60%;
  border-radius: 50%;
  background: radial-gradient(circle, #fff, #f5f5f5);
  transform: translate(-50%, -50%) translateZ(40px);
  box-shadow: 0 8px 15px rgba(0,0,0,0.4);
}

.content2 {
  text-align: center;
  position: relative;
  padding: 20px;
}

.twenty {
  font-size: 3rem;
  font-weight: bold;
  color: #d4af37;
}

.years {
  font-size: 1.5rem;
  margin-top: -10px;
  color: #333;
}

.location {
  font-size: 0.9rem;
  color: #666;
}
Enter fullscreen mode Exit fullscreen mode

Animations

@keyframes badgePulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.02); }
}

.particle {
  position: absolute;
  width: 5px;
  height: 5px;
  background: white;
  border-radius: 50%;
  opacity: 0;
  animation: sparkle 5s infinite;
}

@keyframes sparkle {
  0% { transform: translateY(0); opacity: 0; }
  50% { opacity: 1; }
  100% { transform: translateY(-50px); opacity: 0; }
}

.click-effect {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  background: rgba(255,215,0,0.5);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.click-effect.active {
  animation: ripple 0.6s ease-out;
}

@keyframes ripple {
  from { width: 0; height: 0; opacity: 0.8; }
  to { width: 200px; height: 200px; opacity: 0; }
}
Enter fullscreen mode Exit fullscreen mode

⚙️ Step 4: JavaScript Interactivity

We’ll add:

  • count-up animation,
  • particle generation,
  • tilt effect,
  • click ripple.
document.addEventListener('DOMContentLoaded', () => {
  const badge = document.querySelector('.badge-container');
  const badge3d = document.createElement('div');
  badge3d.classList.add('badge-3d');

  while (badge.firstChild) badge3d.appendChild(badge.firstChild);
  badge.appendChild(badge3d);

  // Particle creation
  const particles = badge.querySelector('.particles');
  for (let i = 0; i < 20; i++) {
    const p = document.createElement('div');
    p.classList.add('particle');
    p.style.left = `${Math.random() * 100}%`;
    p.style.top = `${Math.random() * 100}%`;
    p.style.animationDelay = `${Math.random() * 5}s`;
    particles.appendChild(p);
  }

  // Count-up
  const num = document.querySelector('.twenty');
  let count = 1;
  const target = 20;
  const interval = setInterval(() => {
    num.textContent = count;
    if (count >= target) clearInterval(interval);
    count++;
  }, 75);

  // Tilt effect
  badge.addEventListener('mousemove', (e) => {
    const rect = badge.getBoundingClientRect();
    const x = (e.clientX - rect.left) / rect.width - 0.5;
    const y = (e.clientY - rect.top) / rect.height - 0.5;
    badge3d.style.transform = `rotateX(${y * 20}deg) rotateY(${x * -20}deg)`;
  });

  badge.addEventListener('mouseleave', () => {
    badge3d.style.transform = 'rotateX(0) rotateY(0)';
  });

  // Ripple on click
  badge.addEventListener('click', () => {
    const ripple = document.querySelector('.click-effect');
    ripple.classList.remove('active');
    void ripple.offsetWidth; // reflow
    ripple.classList.add('active');
  });
});
Enter fullscreen mode Exit fullscreen mode

✅ Wrap-Up

With just HTML, CSS, and vanilla JS, we created a polished, interactive badge that:

  • tilts in 3D,
  • sparkles with particles,
  • animates numbers,
  • pulses with life,
  • features curved text along arcs,
  • and reacts to clicks.

This same technique can be adapted for:

  • Achievement badges,
  • Loyalty program tokens,
  • Event anniversary logos,
  • Gamification UI elements.

Try it out, customize the colors and text, and make it your own! 🚀

Top comments (0)