diff --git a/docs/superpowers/plans/2026-06-05-cmusic-frontend.md b/docs/superpowers/plans/2026-06-05-cmusic-frontend.md
new file mode 100644
index 0000000..b066613
--- /dev/null
+++ b/docs/superpowers/plans/2026-06-05-cmusic-frontend.md
@@ -0,0 +1,1401 @@
+# CMusic 3D Frontend Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Build a supermodern 3D-animated music website frontend with React Three Fiber, Framer Motion, GSAP, and Tailwind CSS — cyberpunk-luxury aesthetic.
+
+**Architecture:** Single-page React app with distinct section components each managing their own 3D canvas (via R3F) or animation layer (Framer Motion / GSAP). A global `useAudioPlayer` hook drives the persistent player bar state. Stitch MCP generates base UI screens; Context7 provides accurate library API docs.
+
+**Tech Stack:** React 18, Vite 5, Tailwind CSS 3, @react-three/fiber, @react-three/drei, Framer Motion, GSAP (+ ScrollTrigger), Stitch MCP (UI generation)
+
+---
+
+## File Map
+
+```
+/srv/CMusic/
+├── index.html
+├── vite.config.js
+├── tailwind.config.js
+├── postcss.config.js
+├── package.json
+└── src/
+ ├── main.jsx
+ ├── App.jsx
+ ├── index.css # global styles, CSS vars
+ ├── data/
+ │ └── mockData.js # tracks, albums, artists
+ ├── hooks/
+ │ └── useAudioPlayer.js # global player state
+ └── components/
+ ├── Navbar/
+ │ └── Navbar.jsx
+ ├── Hero/
+ │ ├── Hero.jsx
+ │ ├── AudioVisualizer3D.jsx # R3F bar visualizer
+ │ └── HeroParticles.jsx # R3F floating particles
+ ├── FeaturedAlbums/
+ │ ├── FeaturedAlbums.jsx
+ │ └── AlbumCard3D.jsx # R3F card with flip
+ ├── TrendingTracks/
+ │ ├── TrendingTracks.jsx
+ │ └── TrackRow.jsx
+ ├── ArtistSpotlight/
+ │ ├── ArtistSpotlight.jsx
+ │ └── ArtistParticles.jsx # R3F particle field
+ └── Player/
+ ├── PlayerBar.jsx
+ └── Waveform3D.jsx # R3F animated waveform
+```
+
+---
+
+## Task 1: Scaffold Project
+
+**Files:**
+- Create: `package.json`, `vite.config.js`, `tailwind.config.js`, `postcss.config.js`, `index.html`, `src/main.jsx`
+
+- [ ] **Step 1: Init Vite React project**
+
+```bash
+cd /srv/CMusic
+npm create vite@latest . -- --template react
+```
+
+- [ ] **Step 2: Install all dependencies**
+
+```bash
+npm install
+npm install three @react-three/fiber @react-three/drei
+npm install framer-motion gsap
+npm install tailwindcss@3 postcss autoprefixer
+npm install @types/three
+npx tailwindcss init -p
+```
+
+- [ ] **Step 3: Configure tailwind.config.js**
+
+```js
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ['./index.html', './src/**/*.{js,jsx}'],
+ theme: {
+ extend: {
+ colors: {
+ 'neon-purple': '#b829f5',
+ 'neon-cyan': '#00f5ff',
+ 'neon-pink': '#ff2d78',
+ 'dark-base': '#050508',
+ 'dark-card': '#0d0d18',
+ 'dark-surface': '#12121f',
+ 'glass': 'rgba(255,255,255,0.04)',
+ },
+ fontFamily: {
+ sans: ['Inter', 'system-ui', 'sans-serif'],
+ display: ['Space Grotesk', 'system-ui', 'sans-serif'],
+ },
+ backgroundImage: {
+ 'gradient-neon': 'linear-gradient(135deg, #b829f5 0%, #00f5ff 100%)',
+ 'gradient-dark': 'linear-gradient(180deg, #050508 0%, #0d0d18 100%)',
+ },
+ boxShadow: {
+ 'neon-purple': '0 0 20px rgba(184,41,245,0.5), 0 0 60px rgba(184,41,245,0.2)',
+ 'neon-cyan': '0 0 20px rgba(0,245,255,0.5), 0 0 60px rgba(0,245,255,0.2)',
+ 'glass': 'inset 0 1px 0 rgba(255,255,255,0.1)',
+ },
+ },
+ },
+ plugins: [],
+}
+```
+
+- [ ] **Step 4: Configure vite.config.js**
+
+```js
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+ plugins: [react()],
+})
+```
+
+- [ ] **Step 5: Commit**
+
+```bash
+git init
+git add .
+git commit -m "feat: scaffold Vite+React+R3F+Tailwind project"
+```
+
+---
+
+## Task 2: Fetch Docs via Context7
+
+**Purpose:** Get accurate API signatures for React Three Fiber, Framer Motion, GSAP ScrollTrigger before writing 3D/animation code.
+
+- [ ] **Step 1: Resolve Context7 library IDs**
+
+Use `mcp__context7__resolve-library-id` with queries:
+- `"react-three-fiber 3D scene"`
+- `"framer-motion animation"`
+- `"gsap scroll trigger"`
+
+- [ ] **Step 2: Query R3F docs**
+
+Use `mcp__context7__query-docs` with resolved ID and question:
+`"How to create a Canvas with useFrame animation loop and mesh geometry in React Three Fiber?"`
+
+- [ ] **Step 3: Query Framer Motion docs**
+
+Use `mcp__context7__query-docs` with question:
+`"How to use motion.div with variants, whileHover, and useInView scroll animations?"`
+
+- [ ] **Step 4: Query GSAP ScrollTrigger docs**
+
+Use `mcp__context7__query-docs` with question:
+`"How to use GSAP ScrollTrigger with React useEffect for parallax and scrub animations?"`
+
+---
+
+## Task 3: Global Styles + Design System
+
+**Files:**
+- Modify: `src/index.css`
+- Use Stitch MCP to generate design system
+
+- [ ] **Step 1: Create Stitch design system**
+
+Call `mcp__stitch__create_design_system` with:
+```json
+{
+ "name": "CMusic Design System",
+ "description": "Cyberpunk-luxury dark theme. Deep black backgrounds (#050508), electric purple (#b829f5) and cyan (#00f5ff) neon accents, glassmorphism cards with backdrop-blur, Space Grotesk display font, Inter body. Neon glow box-shadows. No white backgrounds anywhere."
+}
+```
+
+- [ ] **Step 2: Write src/index.css**
+
+```css
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Space+Grotesk:wght@400;500;600;700&display=swap');
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+ --neon-purple: #b829f5;
+ --neon-cyan: #00f5ff;
+ --neon-pink: #ff2d78;
+ --dark-base: #050508;
+ --dark-card: #0d0d18;
+}
+
+* { box-sizing: border-box; }
+
+html { scroll-behavior: smooth; }
+
+body {
+ background-color: var(--dark-base);
+ color: #e8e8f0;
+ font-family: 'Inter', sans-serif;
+ overflow-x: hidden;
+}
+
+::-webkit-scrollbar { width: 4px; }
+::-webkit-scrollbar-track { background: var(--dark-base); }
+::-webkit-scrollbar-thumb {
+ background: var(--neon-purple);
+ border-radius: 2px;
+}
+
+.glass {
+ background: rgba(255, 255, 255, 0.04);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+}
+
+.neon-text-purple {
+ color: var(--neon-purple);
+ text-shadow: 0 0 10px rgba(184,41,245,0.8);
+}
+
+.neon-text-cyan {
+ color: var(--neon-cyan);
+ text-shadow: 0 0 10px rgba(0,245,255,0.8);
+}
+
+.neon-border {
+ border: 1px solid rgba(184,41,245,0.4);
+ box-shadow: 0 0 15px rgba(184,41,245,0.2), inset 0 0 15px rgba(184,41,245,0.05);
+}
+
+canvas { display: block; }
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add src/index.css tailwind.config.js
+git commit -m "feat: global styles and cyberpunk design system"
+```
+
+---
+
+## Task 4: Mock Data + useAudioPlayer Hook
+
+**Files:**
+- Create: `src/data/mockData.js`
+- Create: `src/hooks/useAudioPlayer.js`
+
+- [ ] **Step 1: Write mockData.js**
+
+```js
+export const tracks = [
+ { id: 1, title: 'Neon Pulse', artist: 'Synthwave X', album: 'Electric Dreams', duration: '3:42', plays: '2.1M', cover: 'https://picsum.photos/seed/track1/400/400' },
+ { id: 2, title: 'Void Walker', artist: 'Dark Matter', album: 'Singularity', duration: '4:15', plays: '1.8M', cover: 'https://picsum.photos/seed/track2/400/400' },
+ { id: 3, title: 'Cyber Rain', artist: 'Neo Tokyo', album: 'Ghost Protocol', duration: '3:58', plays: '3.4M', cover: 'https://picsum.photos/seed/track3/400/400' },
+ { id: 4, title: 'Quantum Drift', artist: 'Parallax', album: 'Infinite Loop', duration: '5:02', plays: '987K', cover: 'https://picsum.photos/seed/track4/400/400' },
+ { id: 5, title: 'Aurora Blaze', artist: 'Synthwave X', album: 'Electric Dreams', duration: '4:33', plays: '1.2M', cover: 'https://picsum.photos/seed/track5/400/400' },
+ { id: 6, title: 'Digital Soul', artist: 'Echo System', album: 'Phase Shift', duration: '3:21', plays: '756K', cover: 'https://picsum.photos/seed/track6/400/400' },
+];
+
+export const albums = [
+ { id: 1, title: 'Electric Dreams', artist: 'Synthwave X', year: 2024, genre: 'Synthwave', cover: 'https://picsum.photos/seed/album1/600/600', tracks: 12, color: '#b829f5' },
+ { id: 2, title: 'Singularity', artist: 'Dark Matter', year: 2024, genre: 'Dark Electro', cover: 'https://picsum.photos/seed/album2/600/600', tracks: 9, color: '#00f5ff' },
+ { id: 3, title: 'Ghost Protocol', artist: 'Neo Tokyo', year: 2023, genre: 'Cyberpunk', cover: 'https://picsum.photos/seed/album3/600/600', tracks: 11, color: '#ff2d78' },
+ { id: 4, title: 'Infinite Loop', artist: 'Parallax', year: 2024, genre: 'Ambient', cover: 'https://picsum.photos/seed/album4/600/600', tracks: 8, color: '#b829f5' },
+];
+
+export const artists = [
+ { id: 1, name: 'Synthwave X', genre: 'Synthwave / Electro', followers: '4.2M', image: 'https://picsum.photos/seed/artist1/800/1000', bio: 'Pioneering the neon-drenched soundscapes of tomorrow.' },
+ { id: 2, name: 'Dark Matter', genre: 'Dark Electro', followers: '2.8M', image: 'https://picsum.photos/seed/artist2/800/1000', bio: 'Where darkness meets digital frequency.' },
+];
+```
+
+- [ ] **Step 2: Write useAudioPlayer.js**
+
+```js
+import { useState, useCallback } from 'react'
+import { tracks } from '../data/mockData'
+
+export function useAudioPlayer() {
+ const [currentTrack, setCurrentTrack] = useState(tracks[0])
+ const [isPlaying, setIsPlaying] = useState(false)
+ const [progress, setProgress] = useState(0)
+ const [volume, setVolume] = useState(0.8)
+
+ const play = useCallback((track) => {
+ setCurrentTrack(track)
+ setIsPlaying(true)
+ }, [])
+
+ const togglePlay = useCallback(() => {
+ setIsPlaying(p => !p)
+ }, [])
+
+ const next = useCallback(() => {
+ const idx = tracks.findIndex(t => t.id === currentTrack.id)
+ setCurrentTrack(tracks[(idx + 1) % tracks.length])
+ }, [currentTrack])
+
+ const prev = useCallback(() => {
+ const idx = tracks.findIndex(t => t.id === currentTrack.id)
+ setCurrentTrack(tracks[(idx - 1 + tracks.length) % tracks.length])
+ }, [currentTrack])
+
+ return { currentTrack, isPlaying, progress, volume, play, togglePlay, next, prev, setProgress, setVolume }
+}
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add src/data/mockData.js src/hooks/useAudioPlayer.js
+git commit -m "feat: mock data and audio player hook"
+```
+
+---
+
+## Task 5: Navbar
+
+**Files:**
+- Create: `src/components/Navbar/Navbar.jsx`
+
+- [ ] **Step 1: Generate Navbar screen via Stitch**
+
+Call `mcp__stitch__create_project` then `mcp__stitch__generate_screen_from_text` with:
+```
+"Glassmorphism music app navbar. Dark background with backdrop-blur. Logo 'CMUSIC' in electric purple neon. Nav links: Discover, Artists, Albums, Charts. Right side: Search icon, notification bell, user avatar circle. Subtle purple neon bottom border that animates on scroll. Cyberpunk aesthetic."
+```
+
+- [ ] **Step 2: Write Navbar.jsx**
+
+```jsx
+import { useState, useEffect } from 'react'
+import { motion } from 'framer-motion'
+
+const navLinks = ['Discover', 'Artists', 'Albums', 'Charts']
+
+export default function Navbar() {
+ const [scrolled, setScrolled] = useState(false)
+
+ useEffect(() => {
+ const handler = () => setScrolled(window.scrollY > 20)
+ window.addEventListener('scroll', handler)
+ return () => window.removeEventListener('scroll', handler)
+ }, [])
+
+ return (
+
+
+ {/* Logo */}
+
+ CMUSIC
+
+
+ {/* Links */}
+
+ {navLinks.map((link) => (
+
+ {link}
+
+
+ ))}
+
+
+ {/* Right Actions */}
+
+
+
+ )
+}
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add src/components/Navbar/
+git commit -m "feat: glassmorphism navbar with scroll effect"
+```
+
+---
+
+## Task 6: Hero Section with 3D Visualizer
+
+**Files:**
+- Create: `src/components/Hero/Hero.jsx`
+- Create: `src/components/Hero/AudioVisualizer3D.jsx`
+- Create: `src/components/Hero/HeroParticles.jsx`
+
+- [ ] **Step 1: Write AudioVisualizer3D.jsx**
+
+```jsx
+import { useRef, useMemo } from 'react'
+import { Canvas, useFrame } from '@react-three/fiber'
+import * as THREE from 'three'
+
+const BAR_COUNT = 64
+
+function Bars({ isPlaying }) {
+ const groupRef = useRef()
+ const barsRef = useRef([])
+ const phases = useMemo(() => Array.from({ length: BAR_COUNT }, (_, i) => Math.random() * Math.PI * 2), [])
+ const speeds = useMemo(() => Array.from({ length: BAR_COUNT }, () => 0.8 + Math.random() * 1.5), [])
+
+ useFrame(({ clock }) => {
+ const t = clock.getElapsedTime()
+ barsRef.current.forEach((mesh, i) => {
+ if (!mesh) return
+ const base = isPlaying
+ ? Math.abs(Math.sin(t * speeds[i] + phases[i])) * 1.8 + 0.1
+ : Math.abs(Math.sin(t * 0.3 + phases[i])) * 0.3 + 0.05
+ mesh.scale.y = THREE.MathUtils.lerp(mesh.scale.y, base, 0.12)
+ mesh.position.y = mesh.scale.y * 0.5
+ const hue = (i / BAR_COUNT) * 0.2 + 0.75 + Math.sin(t * 0.2) * 0.05
+ mesh.material.color.setHSL(hue, 1, 0.6)
+ mesh.material.emissive.setHSL(hue, 1, 0.3)
+ })
+ })
+
+ const bars = useMemo(() => {
+ return Array.from({ length: BAR_COUNT }, (_, i) => {
+ const angle = (i / BAR_COUNT) * Math.PI * 2
+ const radius = 3
+ return {
+ x: Math.cos(angle) * radius,
+ z: Math.sin(angle) * radius,
+ }
+ })
+ }, [])
+
+ return (
+
+ {bars.map((pos, i) => (
+ (barsRef.current[i] = el)}
+ position={[pos.x, 0.5, pos.z]}
+ >
+
+
+
+ ))}
+
+
+
+
+
+ )
+}
+
+function RotatingGroup({ isPlaying, children }) {
+ const groupRef = useRef()
+ useFrame((_, delta) => {
+ if (groupRef.current) {
+ groupRef.current.rotation.y += delta * (isPlaying ? 0.4 : 0.1)
+ }
+ })
+ return {children}
+}
+
+export default function AudioVisualizer3D({ isPlaying }) {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+```
+
+- [ ] **Step 2: Write HeroParticles.jsx**
+
+```jsx
+import { useRef, useMemo } from 'react'
+import { Canvas, useFrame } from '@react-three/fiber'
+import * as THREE from 'three'
+
+const PARTICLE_COUNT = 200
+
+function Particles() {
+ const pointsRef = useRef()
+ const positions = useMemo(() => {
+ const arr = new Float32Array(PARTICLE_COUNT * 3)
+ for (let i = 0; i < PARTICLE_COUNT; i++) {
+ arr[i * 3] = (Math.random() - 0.5) * 20
+ arr[i * 3 + 1] = (Math.random() - 0.5) * 20
+ arr[i * 3 + 2] = (Math.random() - 0.5) * 20
+ }
+ return arr
+ }, [])
+
+ useFrame(({ clock }) => {
+ if (pointsRef.current) {
+ pointsRef.current.rotation.y = clock.getElapsedTime() * 0.04
+ pointsRef.current.rotation.x = Math.sin(clock.getElapsedTime() * 0.02) * 0.1
+ }
+ })
+
+ return (
+
+
+
+
+
+
+ )
+}
+
+export default function HeroParticles() {
+ return (
+
+
+
+ )
+}
+```
+
+- [ ] **Step 3: Write Hero.jsx**
+
+```jsx
+import { useEffect, useRef, useState } from 'react'
+import { motion } from 'framer-motion'
+import { gsap } from 'gsap'
+import AudioVisualizer3D from './AudioVisualizer3D'
+import HeroParticles from './HeroParticles'
+
+export default function Hero({ isPlaying, onTogglePlay }) {
+ const headingRef = useRef()
+ const subRef = useRef()
+
+ useEffect(() => {
+ const ctx = gsap.context(() => {
+ gsap.from(headingRef.current, { y: 60, opacity: 0, duration: 1, ease: 'power3.out', delay: 0.3 })
+ gsap.from(subRef.current, { y: 40, opacity: 0, duration: 1, ease: 'power3.out', delay: 0.6 })
+ })
+ return () => ctx.revert()
+ }, [])
+
+ return (
+
+ {/* Particle background */}
+
+
+
+
+ {/* Gradient blobs */}
+
+
+
+ {/* 3D Visualizer */}
+
+
+ {/* Content */}
+
+
+ Now Streaming
+
+
+
+ Feel The {' '}
+ Music
+
+
+ In 3D
+
+
+
+
+ Immerse yourself in a new dimension of music. Visualize every beat, every pulse, every frequency.
+
+
+
+
+ {isPlaying ? '⏸ Pause Vibe' : '▶ Start Listening'}
+
+
+
+ Explore Now
+
+
+
+ {/* Scroll indicator */}
+
+
+
+
+
+
+
+ )
+}
+```
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add src/components/Hero/
+git commit -m "feat: hero section with 3D audio visualizer and particles"
+```
+
+---
+
+## Task 7: Featured Albums with 3D Cards
+
+**Files:**
+- Create: `src/components/FeaturedAlbums/FeaturedAlbums.jsx`
+- Create: `src/components/FeaturedAlbums/AlbumCard3D.jsx`
+
+- [ ] **Step 1: Generate screen via Stitch**
+
+Call `mcp__stitch__generate_screen_from_text` with:
+```
+"Music app featured albums grid. Dark background. 4 album cards with 3D perspective tilt on hover. Each card: album cover image (square), album title, artist name, genre tag. Neon purple/cyan glow border on hover. Glassmorphism card background. 'Featured Albums' section heading with neon accent."
+```
+
+- [ ] **Step 2: Write AlbumCard3D.jsx**
+
+```jsx
+import { useRef } from 'react'
+import { motion, useMotionValue, useSpring, useTransform } from 'framer-motion'
+
+export default function AlbumCard3D({ album, onPlay }) {
+ const cardRef = useRef()
+ const x = useMotionValue(0)
+ const y = useMotionValue(0)
+ const rotateX = useSpring(useTransform(y, [-0.5, 0.5], [15, -15]), { stiffness: 300, damping: 30 })
+ const rotateY = useSpring(useTransform(x, [-0.5, 0.5], [-15, 15]), { stiffness: 300, damping: 30 })
+
+ const handleMouseMove = (e) => {
+ const rect = cardRef.current.getBoundingClientRect()
+ x.set((e.clientX - rect.left) / rect.width - 0.5)
+ y.set((e.clientY - rect.top) / rect.height - 0.5)
+ }
+
+ const handleMouseLeave = () => {
+ x.set(0)
+ y.set(0)
+ }
+
+ return (
+
+ {/* Glow */}
+
+
+ {/* Cover */}
+
+
+
+
+ {/* Play overlay */}
+
+ onPlay(album)}
+ className="w-14 h-14 rounded-full flex items-center justify-center"
+ style={{ background: album.color, boxShadow: `0 0 25px ${album.color}` }}
+ >
+ ▶
+
+
+
+
+ {/* Info */}
+
+
{album.title}
+
{album.artist}
+
+
+ {album.genre}
+
+ {album.tracks} tracks
+
+
+
+ )
+}
+```
+
+- [ ] **Step 3: Write FeaturedAlbums.jsx**
+
+```jsx
+import { useRef } from 'react'
+import { motion, useInView } from 'framer-motion'
+import AlbumCard3D from './AlbumCard3D'
+import { albums } from '../../data/mockData'
+
+export default function FeaturedAlbums({ onPlay }) {
+ const ref = useRef()
+ const inView = useInView(ref, { once: true, margin: '-100px' })
+
+ return (
+
+
+
+
+
+ Collection
+
+ Featured Albums
+
+
+
+
+ {albums.map((album, i) => (
+
+
+
+ ))}
+
+
+
+ )
+}
+```
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add src/components/FeaturedAlbums/
+git commit -m "feat: featured albums with 3D perspective tilt cards"
+```
+
+---
+
+## Task 8: Trending Tracks with Parallax
+
+**Files:**
+- Create: `src/components/TrendingTracks/TrendingTracks.jsx`
+- Create: `src/components/TrendingTracks/TrackRow.jsx`
+
+- [ ] **Step 1: Write TrackRow.jsx**
+
+```jsx
+import { motion } from 'framer-motion'
+
+export default function TrackRow({ track, index, isActive, onPlay }) {
+ return (
+ onPlay(track)}
+ >
+ {/* Rank */}
+
+ {isActive ? '♫' : index + 1}
+
+
+ {/* Cover */}
+
+
+
+ ▶
+
+
+
+ {/* Info */}
+
+
{track.title}
+
{track.artist}
+
+
+ {/* Plays */}
+ {track.plays}
+
+ {/* Duration */}
+ {track.duration}
+
+ {/* Active bar */}
+ {isActive && (
+
+ {[1, 2, 3].map(i => (
+
+ ))}
+
+ )}
+
+ )
+}
+```
+
+- [ ] **Step 2: Write TrendingTracks.jsx**
+
+```jsx
+import { useRef, useEffect } from 'react'
+import { motion, useInView } from 'framer-motion'
+import { gsap } from 'gsap'
+import { ScrollTrigger } from 'gsap/ScrollTrigger'
+import TrackRow from './TrackRow'
+import { tracks } from '../../data/mockData'
+
+gsap.registerPlugin(ScrollTrigger)
+
+export default function TrendingTracks({ currentTrack, onPlay }) {
+ const sectionRef = useRef()
+ const ref = useRef()
+ const inView = useInView(ref, { once: true, margin: '-100px' })
+
+ useEffect(() => {
+ const ctx = gsap.context(() => {
+ gsap.from('.track-parallax', {
+ x: -40,
+ opacity: 0,
+ stagger: 0.08,
+ scrollTrigger: {
+ trigger: sectionRef.current,
+ start: 'top 80%',
+ end: 'bottom 20%',
+ },
+ })
+ }, sectionRef)
+ return () => ctx.revert()
+ }, [])
+
+ return (
+
+
+
+
+
+ Hot Right Now
+
+ Trending Tracks
+
+
+
+
+ {tracks.map((track, i) => (
+
+
+
+ ))}
+
+
+
+ )
+}
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add src/components/TrendingTracks/
+git commit -m "feat: trending tracks section with GSAP parallax"
+```
+
+---
+
+## Task 9: Artist Spotlight with R3F Particle Field
+
+**Files:**
+- Create: `src/components/ArtistSpotlight/ArtistSpotlight.jsx`
+- Create: `src/components/ArtistSpotlight/ArtistParticles.jsx`
+
+- [ ] **Step 1: Write ArtistParticles.jsx**
+
+```jsx
+import { useRef, useMemo } from 'react'
+import { Canvas, useFrame } from '@react-three/fiber'
+import * as THREE from 'three'
+
+const COUNT = 300
+
+function Particles() {
+ const meshRef = useRef()
+ const dummy = useMemo(() => new THREE.Object3D(), [])
+ const particles = useMemo(() => {
+ return Array.from({ length: COUNT }, (_, i) => ({
+ phi: Math.acos(-1 + (2 * i) / COUNT),
+ theta: Math.sqrt(COUNT * Math.PI) * (2 * i / COUNT) * Math.PI,
+ speed: 0.2 + Math.random() * 0.3,
+ radius: 2 + Math.random() * 1.5,
+ }))
+ }, [])
+
+ useFrame(({ clock }) => {
+ const t = clock.getElapsedTime()
+ particles.forEach((p, i) => {
+ const r = p.radius + Math.sin(t * p.speed + i) * 0.3
+ dummy.position.set(
+ r * Math.sin(p.phi) * Math.cos(p.theta + t * p.speed * 0.1),
+ r * Math.cos(p.phi),
+ r * Math.sin(p.phi) * Math.sin(p.theta + t * p.speed * 0.1)
+ )
+ dummy.scale.setScalar(0.015 + Math.abs(Math.sin(t + i)) * 0.01)
+ dummy.updateMatrix()
+ meshRef.current.setMatrixAt(i, dummy.matrix)
+ })
+ meshRef.current.instanceMatrix.needsUpdate = true
+ })
+
+ return (
+
+
+
+
+ )
+}
+
+export default function ArtistParticles() {
+ return (
+
+
+
+ )
+}
+```
+
+- [ ] **Step 2: Write ArtistSpotlight.jsx**
+
+```jsx
+import { useRef } from 'react'
+import { motion, useInView } from 'framer-motion'
+import ArtistParticles from './ArtistParticles'
+import { artists } from '../../data/mockData'
+
+export default function ArtistSpotlight() {
+ const artist = artists[0]
+ const ref = useRef()
+ const inView = useInView(ref, { once: true, margin: '-100px' })
+
+ return (
+
+
+
+ {/* Particle field */}
+
+
+ {/* Gradient overlay */}
+
+
+
+
+ {/* Text */}
+
+ Artist Spotlight
+
+ {artist.name}
+
+ {artist.genre}
+ {artist.bio}
+
+
+
+
{artist.followers}
+
Followers
+
+
+
+
+
+
+
+
+ Follow Artist
+
+
+
+ {/* Artist image */}
+
+
+
+
+
+
+
+
+
+ )
+}
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add src/components/ArtistSpotlight/
+git commit -m "feat: artist spotlight with R3F particle sphere"
+```
+
+---
+
+## Task 10: Persistent Player Bar with 3D Waveform
+
+**Files:**
+- Create: `src/components/Player/Waveform3D.jsx`
+- Create: `src/components/Player/PlayerBar.jsx`
+
+- [ ] **Step 1: Write Waveform3D.jsx**
+
+```jsx
+import { useRef, useMemo } from 'react'
+import { Canvas, useFrame } from '@react-three/fiber'
+import * as THREE from 'three'
+
+const BAR_COUNT = 32
+
+function Waveform({ isPlaying }) {
+ const barsRef = useRef([])
+ const phases = useMemo(() => Array.from({ length: BAR_COUNT }, () => Math.random() * Math.PI * 2), [])
+
+ useFrame(({ clock }) => {
+ const t = clock.getElapsedTime()
+ barsRef.current.forEach((mesh, i) => {
+ if (!mesh) return
+ const target = isPlaying
+ ? Math.abs(Math.sin(t * 3 + phases[i])) * 0.8 + 0.05
+ : 0.05 + Math.abs(Math.sin(t * 0.5 + phases[i])) * 0.1
+ mesh.scale.y = THREE.MathUtils.lerp(mesh.scale.y, target, 0.15)
+ mesh.position.y = mesh.scale.y * 0.5
+ })
+ })
+
+ return (
+
+ {Array.from({ length: BAR_COUNT }, (_, i) => (
+ (barsRef.current[i] = el)}
+ position={[(i - BAR_COUNT / 2) * 0.07, 0.05, 0]}
+ >
+
+
+
+ ))}
+
+ )
+}
+
+export default function Waveform3D({ isPlaying }) {
+ return (
+
+
+
+ )
+}
+```
+
+- [ ] **Step 2: Write PlayerBar.jsx**
+
+```jsx
+import { motion, AnimatePresence } from 'framer-motion'
+import Waveform3D from './Waveform3D'
+
+export default function PlayerBar({ currentTrack, isPlaying, progress, volume, onToggle, onNext, onPrev, onSeek, onVolume }) {
+ if (!currentTrack) return null
+
+ return (
+
+
+
+ {/* Track info */}
+
+
+
+ {isPlaying && (
+
+ )}
+
+
+
{currentTrack.title}
+
{currentTrack.artist}
+
+
+
+ {/* Center: controls + waveform */}
+
+ {/* Controls */}
+
+
+
+
+
+
+
+
+ {isPlaying ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+ {/* Progress bar */}
+
+
0:00
+
{
+ const rect = e.currentTarget.getBoundingClientRect()
+ onSeek((e.clientX - rect.left) / rect.width)
+ }}
+ >
+
+
+
{currentTrack.duration}
+
+
+
+ {/* Right: waveform + volume */}
+
+
+
+
+
+
+
+
+
onVolume(parseFloat(e.target.value))}
+ className="w-20 accent-neon-purple"
+ />
+
+
+
+
+
+ )
+}
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add src/components/Player/
+git commit -m "feat: persistent player bar with 3D waveform"
+```
+
+---
+
+## Task 11: Assemble App + Run
+
+**Files:**
+- Modify: `src/App.jsx`
+- Modify: `src/main.jsx`
+
+- [ ] **Step 1: Write src/main.jsx**
+
+```jsx
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.jsx'
+
+createRoot(document.getElementById('root')).render(
+
+
+
+)
+```
+
+- [ ] **Step 2: Write src/App.jsx**
+
+```jsx
+import { useState } from 'react'
+import { useAudioPlayer } from './hooks/useAudioPlayer'
+import Navbar from './components/Navbar/Navbar'
+import Hero from './components/Hero/Hero'
+import FeaturedAlbums from './components/FeaturedAlbums/FeaturedAlbums'
+import TrendingTracks from './components/TrendingTracks/TrendingTracks'
+import ArtistSpotlight from './components/ArtistSpotlight/ArtistSpotlight'
+import PlayerBar from './components/Player/PlayerBar'
+
+export default function App() {
+ const player = useAudioPlayer()
+
+ return (
+
+ )
+}
+```
+
+- [ ] **Step 3: Update index.html with correct title**
+
+```html
+
+
+
+
+
+
+ CMusic — Feel The Music In 3D
+
+
+
+
+
+
+```
+
+- [ ] **Step 4: Start dev server and verify**
+
+```bash
+npm run dev
+```
+Expected: Server starts at http://localhost:5173. Verify all sections render, 3D visualizer animates, player bar appears, album cards tilt on hover.
+
+- [ ] **Step 5: Final commit**
+
+```bash
+git add src/App.jsx src/main.jsx index.html
+git commit -m "feat: assemble full CMusic 3D music website"
+```