A living Nuxt-based website that reacts to the world's mood. Through real-time sentiment analysis of news and social media, the site's visual expression — colors, animations, and atmosphere — changes automatically.
🌐 Live Site: https://emotionwave.iamjarl.com (Vercel)
Create a data-driven artwork where the website changes in real-time based on global sentiment. Users get a visual sense of "how the world feels" through an immersive experience combining visual and audio elements.
- Framework: Nuxt 4.4.8 + Vite
- UI: TailwindCSS 4.1.18
- Visuals: Three.js 0.182.0 for particle effects (lazy loaded)
- Audio: Tone.js 15.1.22 for generative ambient sound (lazy loaded)
- Data: Multi-source sentiment analysis (GDELT API, NewsAPI, Reddit)
- PWA: Service Worker for offline functionality
- TypeScript: Full type safety throughout the application
- Dynamic particle system that reacts to sentiment (1000-2000 particles based on device)
- Color transitions based on mood with smooth animations
- Emotion-aware palette: when emotion analysis is available, colors blend distinct hues for anger, fear, joy, sadness, surprise and disgust instead of a single negative-positive axis
- Interactive mouse-based particle movement
- Smooth 60fps animations and transitions
- Responsive design with mobile optimization
- GPU-accelerated rendering with WebGL
- Live headline display: Rotating article headlines centered on screen, updating every 5 seconds
- Generative ambient music using Tone.js with high variation
- Musical scale changes based on sentiment:
- Negative (-1 to -0.5): C minor, low tones
- Neutral-negative (-0.5 to 0): Eb major, ambient
- Neutral-positive (0 to 0.5): F major, flowing
- Positive (0.5 to 1): C major, uplifting
- Multiple chord variations: 4-5 different chord progressions per sentiment range
- Varied timing: Dynamic intervals (3-6 seconds) between musical phrases
- Rich melodic content: Additional notes, arpeggios, and bass lines
- Dynamic Timbre: Instrument character (oscillator/envelope) morphs between textures (Sharp/Soft) based on mood
- Dynamic effects: Filter and reverb adjust based on sentiment with variation
- Reverb and delay effects for atmospheric sound
- Volume control with smooth transitions
- Reactive music updates when sentiment changes during playback
- User interaction required to start audio (browser policy)
- Multi-source aggregation: Combines data from GDELT API, NewsAPI (optional), and Reddit
- Emotion categories: headlines are also classified into seven emotions (Ekman 6 + neutral) and aggregated into a "world emotion" that drives the palette and musical scale
- Advanced analysis: Optional HuggingFace Inference API integration for improved sentiment detection
- Strategically used on top 10 articles for balance between accuracy and performance
- Remaining articles use fast keyword-based analysis
- Keyword-based fallback: Enhanced keyword analysis when APIs don't provide explicit sentiment scores
- Reddit integration: Social media sentiment with full weight for valuable real-world mood signals
- Real-time analysis of news articles from multiple sources
- Intensity-weighted average sentiment across all sources (prioritizing strong emotional signals)
- Filters out articles with zero sentiment (missing data) for improved accuracy
- Retry logic with exponential backoff for reliable data fetching
- 30-second cache to limit API calls
- Sentiment score range: -1 (negative) to +1 (positive)
- Smooth animated transitions between sentiment scores
- Automatic fallback to dynamic time-based data if APIs fail
- Graceful degradation: works even if some APIs are unavailable
- Article headlines: API returns article titles for live display on the site
- Offline functionality with service worker
- Installable on mobile devices
- Fullscreen experience
- Optimized for mobile performance
- Comprehensive meta tags (Open Graph, Twitter Cards)
- Structured data (JSON-LD) for rich snippets
- Dynamic sitemap.xml and robots.txt generation
- Canonical URLs for proper indexing
- Optimized favicon set for all platforms
| Score | Visual Mood | Audio Mood | Background Color |
|---|---|---|---|
-1 to -0.5 |
Dark, uneasy, flickering | C minor, low tones | Dark gray (#111827) |
-0.5 to 0 |
Cold, rain-like particles | Eb major, ambient | Dark blue (#1e3a8a) |
0 to 0.5 |
Neutral, light gradient | F major, flowing | Blue (#3b82f6) |
0.5 to 1 |
Warm, bubbling, colorful | C major, uplifting | Yellow (#eab308) |
- Node.js 20.x or higher
- npm or yarn
- Clone the repository:
git clone https://github.com/JarlLyng/EmotionWave.git
cd EmotionWave- Install dependencies:
npm install- Create a
.envfile (optional):
NUXT_PUBLIC_SITE_URL=http://localhost:3000
NUXT_PUBLIC_BASE_URL=/- Start the development server:
npm run devThe application will be available at http://localhost:3000
npm run dev- Start development server with hot-reloadnpm run build- Build for production (SSR)npm run generate- Generate static site for deploymentnpm run preview- Preview production build locally
- ARCHITECTURE.md - Technical architecture and component structure
- DEPLOYMENT.md - Deployment guides for various platforms
- CONFIGURATION.md - Configuration options and environment variables
- CONTRIBUTING.md - Guidelines for contributing
-
Static Hosting Limitations
- Server-side API routes don't work on static hosting (GitHub Pages, etc.)
- Application uses fallback data automatically on static hosting
- For full functionality with HuggingFace sentiment analysis, deploy to Vercel
-
Audio Autoplay
- Browser policies require user interaction to start audio
- User must click the sound button to enable audio
-
GDELT API Rate Limits
- May be rate-limited during high traffic
- Fallback data ensures application always works
- No sensitive data exposed in client-side code
- API keys stored as GitHub secrets
- Service worker uses secure context
- HTTPS required for PWA features
Performance-focused by design:
- Lazy loading: Three.js and Tone.js are loaded on demand, kept out of the initial bundle
- GPU-accelerated rendering: WebGL with device-aware particle counts (1000 on mobile, 2000 on desktop)
- Server-side caching: 30-second cache on the sentiment API to limit upstream calls
- Bundle size: ~635KB gzipped production build
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
This project is licensed under the MIT License - see the LICENSE file for details.
- GDELT for news data
- Tone.js for audio synthesis
- Three.js for 3D graphics and particle effects
- Nuxt team for the amazing framework
For issues, questions, or contributions, please open an issue on GitHub.
Built with ❤️ using Nuxt 4, Vue 3, Three.js, and Tone.js
