Piano

Interactive piano visualization using Framer Motion for dynamic bar height and color changes based on hover

Installation

Run the following command

It will create a new file piano.tsx inside the components/cards/piano.tsx directory.

mkdir -p components/cards && touch components/cards/piano.tsx

Paste the code

Open the newly created file and paste the following code:

"use client";
 
import React, { useState } from "react";
import { motion } from "framer-motion";
 
const PianoVisualization: React.FC = () => {
  const [activeIndex, setActiveIndex] = useState<number | null>(null);
 
  const handleMouseEnter = (index: number) => {
    setActiveIndex(index);
  };
 
  const handleMouseLeave = () => {
    setActiveIndex(null);
  };
 
  return (
    <div className="center size-full">
      <div className="flex items-end h-60">
        {[...Array(30)].map((_, index) => {
          // Calculate the height based on proximity to the active index
          let baseHeight = 100; // Base height for bars in pixels
          const proximity =
            activeIndex !== null ? Math.abs(activeIndex - index) : 0;
 
          // Hovered bar has max height, with a decreasing ratio for its neighbors
          let height =
            activeIndex === null
              ? baseHeight // default height when nothing is hovered
              : proximity === 0
              ? 140 // Height of the hovered bar
              : proximity === 1
              ? 120 // First neighbors' height
              : proximity === 2
              ? 110 // Second neighbors' height
              : 100; // Default height for other bars
 
          return (
            <motion.div
              key={index}
              className="bg-gray-400 w-4 mx-0.5 rounded-2xl"
              onMouseEnter={() => handleMouseEnter(index)}
              onMouseLeave={handleMouseLeave}
              animate={{
                height: `${height}px`, // Animate height dynamically
                backgroundColor:
                  activeIndex !== null && proximity <= 2
                    ? "#3B82F6" // Change color of hovered and adjacent bars
                    : "#6B7280", // Default color
              }}
              transition={{
                type: "spring",
                stiffness: 200,
                damping: 20,
              }}
            />
          );
        })}
      </div>
    </div>
  );
};
 
export default PianoVisualization;

Credits

Built by Bossadi Zenith