Real-time synchronized lyrics overlay for macOS. Kivo detects what's currently playing on Spotify or Apple Music, fetches matching lyrics, and displays them in sync as a floating overlay or a Dynamic Island-style widget.
- Auto-detection -- Automatically detects the currently playing track from Spotify and Apple Music
- Two display modes
- Overlay -- Frosted-glass floating panel with scrolling lyrics
- Dynamic Island -- Compact pill at the top of your screen that expands on hover with artwork, lyrics, progress bar, and playback controls
- Playback controls -- Play/pause, next, previous, and seek directly from the Dynamic Island
- 9 theme presets -- Neon Green, Sunset, Cyberpunk, Ocean, Minimal, Rose, Aurora, Amber, and Custom
- Highly configurable -- Font size, colors, background opacity, glow intensity, overlay size/position, sync offset, visible line count
- Global shortcut --
Cmd+Shift+Lto toggle visibility - System tray -- Quick access to show/hide and settings
- Smart sync -- Binary search + 50ms interpolation for smooth line-by-line highlighting
- Lyrics caching -- Up to 200 entries to avoid redundant fetches
- Multi-source lyrics -- Fetches from LRCLib (primary) and NetEase Cloud Music (fallback)
- macOS (required -- uses AppleScript for media detection)
- Node.js 18+
- Spotify or Apple Music desktop app
# Clone the repository
git clone https://github.com/6space7/Kivo.git
cd Kivo
# Install dependencies
npm installnpm startThis builds the renderer UI with Vite and launches Electron.
npm run buildThis builds the UI and packages the app with electron-builder. The output .dmg / .app will be in the dist/ directory.
npm run build:uiKivo/
├── main.js # Electron main process (windows, IPC, tray, media orchestration)
├── preload.js # Preload script for the overlay window
├── preload-island.js # Preload script for the Dynamic Island window
├── preload-settings.js # Preload script for the settings window
├── package.json # Project manifest and electron-builder config
├── vite.config.mjs # Vite config with 3 entry points (overlay, settings, island)
│
├── src/ # Main process modules
│ ├── store.js # JSON-file settings persistence
│ ├── themes.js # 9 theme presets
│ ├── lyrics/
│ │ ├── fetcher.js # Multi-source lyrics fetching (LRCLib + NetEase)
│ │ ├── parser.js # LRC format parser
│ │ └── sync.js # Binary-search sync engine
│ └── media/
│ ├── detector.js # Polling-based media detection
│ └── macos.js # AppleScript integration for Spotify/Apple Music
│
├── renderer/ # Frontend (React + Vite)
│ ├── overlay.html # Overlay window entry
│ ├── island.html # Dynamic Island window entry
│ ├── settings.html # Settings window entry
│ └── src/
│ ├── index.css # Global styles (Tailwind + custom tokens)
│ ├── overlay/ # Lyrics overlay UI
│ ├── island/ # Dynamic Island UI
│ └── settings/ # Settings panel UI
│
├── assets/ # App icons and tray icons
└── scripts/ # Icon generation utilities
| Layer | Technology |
|---|---|
| Desktop | Electron 40.x |
| UI | React 19 |
| Build | Vite 6 |
| CSS | Tailwind CSS 4 |
| Animations | Framer Motion |
| Icons | Lucide React |
| Packaging | electron-builder |
| Platform APIs | macOS AppleScript |
| Lyrics | LRCLib API, NetEase Cloud Music API |
All settings are persisted in a JSON file in the Electron userData directory. You can configure:
| Setting | Default | Description |
|---|---|---|
| Display Mode | overlay |
overlay or dynamic-island |
| Theme | neon-green |
One of 9 presets or custom |
| Font Size | 24 |
Range: 12--48 |
| Text Color | #ffffff |
Inactive lyric color |
| Highlight Color | #00ff88 |
Active lyric color |
| Background Opacity | 0.3 |
Range: 0--1 |
| Glow Intensity | 1.0 |
Range: 0--1.5 |
| Overlay Position | bottom-center |
top-center, bottom-center, or custom |
| Overlay Width | 700 |
Range: 300--1200 |
| Overlay Height | 260 |
Range: 120--600 |
| Sync Offset | 0 |
Range: -5000 to +5000 ms |
| Visible Lines | 5 |
Number of lyric lines to show |