About this snippet

Showcase physical products with an immersive 3D viewer. Moving your mouse across the image smoothly rotates the perspective, which gives customers a premium retail experience.

Size
3.0 KB
Uses
HTML, CSS, JavaScript
Loads
placehold.co
Views
197
<div class="viewer-container">
  <div class="product-info">
    <h2>Quantum Headphones</h2>
    <p>Experience studio-quality sound with adaptive noise cancellation.</p>
  </div>
  
  <div class="product-stage" id="stage">
    <div class="product-wrapper" id="product">
      <!-- Glow Layer -->
      <div class="product-glow"></div>
      
      <!-- The Product -->
      <img src="https://placehold.co/400x400/1e293b/ffffff?text=Headphones" alt="Product" class="product-img">
      
      <!-- Floating Badge -->
      <div class="floating-badge">Pro Edition</div>
    </div>
  </div>
</div>
*, *::before, *::after {
  box-sizing: border-box;
}

body {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  margin: 0;
  background: #f1f5f9;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
}

.viewer-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;
  width: 100%;
}

.product-info {
  text-align: center;
  color: #0f172a;
}

.product-info h2 {
  font-size: 2.5rem;
  margin: 0 0 10px 0;
  letter-spacing: -1px;
}

.product-info p {
  color: #64748b;
  font-size: 1.125rem;
  margin: 0;
}

.product-stage {
  perspective: 1200px;
  width: 100%;
  max-width: 500px;
  height: 300px;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: crosshair;
}

.product-wrapper {
  position: relative;
  width: 300px;
  height: 300px;
  transform-style: preserve-3d;
  will-change: transform;
}

.product-glow {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200px;
  height: 200px;
  background: #3b82f6;
  border-radius: 50%;
  filter: blur(60px);
  transform: translate(-50%, -50%) translateZ(-100px);
  opacity: 0.5;
}

.product-img {
  width: 100%;
  height: 100%;
  border-radius: 24px;
  object-fit: cover;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.2);
  transform: translateZ(20px);
  border: 4px solid #ffffff;
}

.floating-badge {
  position: absolute;
  bottom: -20px;
  right: -20px;
  background: #10b981;
  color: #ffffff;
  padding: 10px 20px;
  border-radius: 30px;
  font-weight: 700;
  font-size: 1rem;
  box-shadow: 0 10px 20px rgba(16, 185, 129, 0.4);
  transform: translateZ(60px);
}
const stage = document.getElementById("stage");
const product = document.getElementById("product");

stage.addEventListener("mousemove", function(e) {
  const rect = stage.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  
  const centerX = rect.width / 2;
  const centerY = rect.height / 2;
  
  const rotateX = ((y - centerY) / centerY) * -20;
  const rotateY = ((x - centerX) / centerX) * 20;
  
  product.style.transform = "rotateX(" + rotateX + "deg) rotateY(" + rotateY + "deg)";
});

stage.addEventListener("mouseleave", function() {
  product.style.transition = "transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)";
  product.style.transform = "rotateX(0deg) rotateY(0deg)";
});

stage.addEventListener("mouseenter", function() {
  product.style.transition = "none";
});
Esc
↑↓ moveEnter select

Start from a template

Open an .html file, or HTML, CSS, JS, Markdown, Less or TypeScript files. You can also drop them here. Styles and scripts go into their panes.

Share

Source

Keyboard shortcuts

Save