Spaces:
Running
Running
Please use bigger set of dictionary for postive and negative word. Use all forms of words as it still is not detecting mod . Bar is always neutral - Initial Deployment
63c37de verified | <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Serenity - Mood-Based Calming Audio</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.18.0/dist/tf.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/universal-sentence-encoder@1.3.3/dist/universal-sentence-encoder.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/howler@2.2.3/dist/howler.min.js"></script> | |
| <style> | |
| .gradient-bg { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| } | |
| .pulse-animation { | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { | |
| transform: scale(0.95); | |
| box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7); | |
| } | |
| 70% { | |
| transform: scale(1); | |
| box-shadow: 0 0 0 10px rgba(255, 255, 255, 0); | |
| } | |
| 100% { | |
| transform: scale(0.95); | |
| box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); | |
| } | |
| } | |
| .waveform { | |
| height: 60px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 12px; | |
| } | |
| .waveform-bar { | |
| background: white; | |
| width: 4px; | |
| height: 20px; | |
| margin: 0 2px; | |
| border-radius: 2px; | |
| animation: equalize 1.5s infinite ease-in-out; | |
| } | |
| @keyframes equalize { | |
| 0%, 100% { | |
| height: 10px; | |
| } | |
| 50% { | |
| height: 30px; | |
| } | |
| } | |
| .waveform-bar:nth-child(1) { animation-delay: 0.1s; } | |
| .waveform-bar:nth-child(2) { animation-delay: 0.3s; } | |
| .waveform-bar:nth-child(3) { animation-delay: 0.5s; } | |
| .waveform-bar:nth-child(4) { animation-delay: 0.2s; } | |
| .waveform-bar:nth-child(5) { animation-delay: 0.4s; } | |
| .waveform-bar:nth-child(6) { animation-delay: 0.6s; } | |
| .waveform-bar:nth-child(7) { animation-delay: 0.3s; } | |
| .waveform-bar:nth-child(8) { animation-delay: 0.1s; } | |
| .waveform-bar:nth-child(9) { animation-delay: 0.5s; } | |
| .waveform-bar:nth-child(10) { animation-delay: 0.2s; } | |
| </style> | |
| </head> | |
| <body class="gradient-bg min-h-screen text-white"> | |
| <div class="container mx-auto px-4 py-12"> | |
| <header class="text-center mb-12"> | |
| <h1 class="text-4xl md:text-5xl font-bold mb-4">Serenity</h1> | |
| <p class="text-xl opacity-90">Real-time mood-based calming audio generator</p> | |
| </header> | |
| <div class="max-w-3xl mx-auto bg-white bg-opacity-10 backdrop-filter backdrop-blur-lg rounded-2xl shadow-xl overflow-hidden"> | |
| <div class="p-8"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-8"> | |
| <!-- Heart Rate Section --> | |
| <div class="bg-white bg-opacity-10 p-6 rounded-xl"> | |
| <h2 class="text-2xl font-semibold mb-4 flex items-center"> | |
| <i class="fas fa-heartbeat mr-3 text-red-400"></i> Heart Rate Monitor | |
| </h2> | |
| <div class="text-center py-6"> | |
| <div class="inline-block relative"> | |
| <div class="w-40 h-40 rounded-full border-4 border-red-400 flex items-center justify-center pulse-animation"> | |
| <span id="heartRateValue" class="text-5xl font-bold">--</span> | |
| <span class="text-xl ml-1">BPM</span> | |
| </div> | |
| <div class="absolute -bottom-2 left-0 right-0 text-center"> | |
| <span class="bg-red-400 text-white px-3 py-1 rounded-full text-sm">LIVE</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-6"> | |
| <button id="connectDeviceBtn" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-3 px-4 rounded-lg font-medium transition duration-300 flex items-center justify-center"> | |
| <i class="fas fa-bluetooth-b mr-2"></i> Connect Smart Device | |
| </button> | |
| <p class="text-sm opacity-80 mt-2 text-center">Or enter manually below</p> | |
| <div class="mt-4 flex"> | |
| <input type="number" id="manualHeartRate" placeholder="Enter BPM" class="flex-1 bg-white bg-opacity-20 border border-white border-opacity-30 rounded-l-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-400"> | |
| <button id="submitManualBtn" class="bg-blue-500 hover:bg-blue-600 px-4 py-2 rounded-r-lg transition duration-300">Submit</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Mood Analysis Section --> | |
| <div class="bg-white bg-opacity-10 p-6 rounded-xl"> | |
| <h2 class="text-2xl font-semibold mb-4 flex items-center"> | |
| <i class="fas fa-brain mr-3 text-purple-400"></i> Mood Analysis | |
| </h2> | |
| <div class="mb-6"> | |
| <label for="moodDescription" class="block mb-2 text-sm font-medium">How are you feeling?</label> | |
| <textarea id="moodDescription" rows="3" class="w-full bg-white bg-opacity-20 border border-white border-opacity-30 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-purple-400" placeholder="Describe your current mood or feelings..."></textarea> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block mb-2 text-sm font-medium">Detected Mood:</label> | |
| <div id="moodIndicator" class="h-4 bg-gradient-to-r from-red-500 via-yellow-500 to-green-500 rounded-full relative"> | |
| <div id="moodPointer" class="absolute w-4 h-4 bg-white rounded-full -top-1 transform -translate-x-2" style="left: 50%;"></div> | |
| </div> | |
| <div class="flex justify-between mt-1 text-xs"> | |
| <span>Stressed</span> | |
| <span>Neutral</span> | |
| <span>Calm</span> | |
| </div> | |
| <div id="moodLabel" class="text-center mt-2 font-medium text-lg">--</div> | |
| </div> | |
| <button id="analyzeBtn" class="w-full bg-purple-500 hover:bg-purple-600 text-white py-3 px-4 rounded-lg font-medium transition duration-300"> | |
| Analyze My Mood | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Audio Generator Section --> | |
| <div class="mt-8 bg-white bg-opacity-10 p-6 rounded-xl"> | |
| <h2 class="text-2xl font-semibold mb-4 flex items-center"> | |
| <i class="fas fa-music mr-3 text-teal-400"></i> Personalized Calming Audio | |
| </h2> | |
| <div id="audioRecommendation" class="mb-6 hidden"> | |
| <div class="bg-white bg-opacity-20 rounded-lg p-4"> | |
| <p class="font-medium mb-2">Recommended for you:</p> | |
| <p id="recommendationText" class="text-sm opacity-90">Based on your heart rate and mood description, we recommend this calming audio sequence.</p> | |
| </div> | |
| </div> | |
| <div class="waveform mb-6 hidden" id="waveform"> | |
| <div class="waveform-bar"></div> | |
| <div class="waveform-bar"></div> | |
| <div class="waveform-bar"></div> | |
| <div class="waveform-bar"></div> | |
| <div class="waveform-bar"></div> | |
| <div class="waveform-bar"></div> | |
| <div class="waveform-bar"></div> | |
| <div class="waveform-bar"></div> | |
| <div class="waveform-bar"></div> | |
| <div class="waveform-bar"></div> | |
| </div> | |
| <div class="flex flex-col sm:flex-row gap-4"> | |
| <button id="generateBtn" class="flex-1 bg-teal-500 hover:bg-teal-600 text-white py-3 px-4 rounded-lg font-medium transition duration-300 flex items-center justify-center"> | |
| <i class="fas fa-play mr-2"></i> Generate Audio | |
| </button> | |
| <button id="stopBtn" class="flex-1 bg-gray-500 hover:bg-gray-600 text-white py-3 px-4 rounded-lg font-medium transition duration-300 flex items-center justify-center" disabled> | |
| <i class="fas fa-stop mr-2"></i> Stop | |
| </button> | |
| </div> | |
| <div class="mt-6 grid grid-cols-2 md:grid-cols-4 gap-3 hidden" id="audioControls"> | |
| <button class="bg-white bg-opacity-20 hover:bg-opacity-30 py-2 px-3 rounded-lg transition duration-300 text-sm flex items-center justify-center"> | |
| <i class="fas fa-volume-up mr-2"></i> Volume | |
| </button> | |
| <button class="bg-white bg-opacity-20 hover:bg-opacity-30 py-2 px-3 rounded-lg transition duration-300 text-sm flex items-center justify-center"> | |
| <i class="fas fa-sliders-h mr-2"></i> EQ | |
| </button> | |
| <button class="bg-white bg-opacity-20 hover:bg-opacity-30 py-2 px-3 rounded-lg transition duration-300 text-sm flex items-center justify-center"> | |
| <i class="fas fa-clock mr-2"></i> Duration | |
| </button> | |
| <button class="bg-white bg-opacity-20 hover:bg-opacity-30 py-2 px-3 rounded-lg transition duration-300 text-sm flex items-center justify-center"> | |
| <i class="fas fa-save mr-2"></i> Save | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- How It Works Section --> | |
| <div class="max-w-3xl mx-auto mt-12 bg-white bg-opacity-10 backdrop-filter backdrop-blur-lg rounded-2xl p-8"> | |
| <h2 class="text-2xl font-semibold mb-6 text-center">How Serenity Works</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-6"> | |
| <div class="text-center"> | |
| <div class="w-16 h-16 bg-blue-500 bg-opacity-30 rounded-full flex items-center justify-center mx-auto mb-4"> | |
| <i class="fas fa-heartbeat text-2xl"></i> | |
| </div> | |
| <h3 class="font-medium mb-2">Biometric Analysis</h3> | |
| <p class="text-sm opacity-80">We analyze your heart rate data to understand your physiological state.</p> | |
| </div> | |
| <div class="text-center"> | |
| <div class="w-16 h-16 bg-purple-500 bg-opacity-30 rounded-full flex items-center justify-center mx-auto mb-4"> | |
| <i class="fas fa-brain text-2xl"></i> | |
| </div> | |
| <h3 class="font-medium mb-2">Mood Detection</h3> | |
| <p class="text-sm opacity-80">Using advanced AI, we interpret your mood from your description.</p> | |
| </div> | |
| <div class="text-center"> | |
| <div class="w-16 h-16 bg-teal-500 bg-opacity-30 rounded-full flex items-center justify-center mx-auto mb-4"> | |
| <i class="fas fa-music text-2xl"></i> | |
| </div> | |
| <h3 class="font-medium mb-2">Audio Generation</h3> | |
| <p class="text-sm opacity-80">We create personalized calming audio tailored to your current needs.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <footer class="text-center py-6 text-white text-opacity-70 text-sm"> | |
| Developed by Sangam S Bhamare 2025 | |
| </footer> | |
| <script> | |
| // DOM Elements | |
| const connectDeviceBtn = document.getElementById('connectDeviceBtn'); | |
| const manualHeartRate = document.getElementById('manualHeartRate'); | |
| const submitManualBtn = document.getElementById('submitManualBtn'); | |
| const heartRateValue = document.getElementById('heartRateValue'); | |
| const moodDescription = document.getElementById('moodDescription'); | |
| const analyzeBtn = document.getElementById('analyzeBtn'); | |
| const moodPointer = document.getElementById('moodPointer'); | |
| const moodLabel = document.getElementById('moodLabel'); | |
| const generateBtn = document.getElementById('generateBtn'); | |
| const stopBtn = document.getElementById('stopBtn'); | |
| const audioRecommendation = document.getElementById('audioRecommendation'); | |
| const recommendationText = document.getElementById('recommendationText'); | |
| const waveform = document.getElementById('waveform'); | |
| const audioControls = document.getElementById('audioControls'); | |
| // Variables | |
| let currentHeartRate = null; | |
| let currentMoodScore = 0.5; // 0-1 scale (0=stressed, 1=calm) | |
| let currentSound = null; | |
| let isPlaying = false; | |
| // Mock device connection | |
| connectDeviceBtn.addEventListener('click', () => { | |
| connectDeviceBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Searching...'; | |
| // Simulate device connection | |
| setTimeout(() => { | |
| connectDeviceBtn.innerHTML = '<i class="fas fa-check-circle mr-2"></i> Connected'; | |
| connectDeviceBtn.classList.remove('bg-blue-500', 'hover:bg-blue-600'); | |
| connectDeviceBtn.classList.add('bg-green-500', 'hover:bg-green-600'); | |
| // Start mock heart rate data | |
| startMockHeartRate(); | |
| }, 2000); | |
| }); | |
| // Manual heart rate submission | |
| submitManualBtn.addEventListener('click', () => { | |
| const rate = parseInt(manualHeartRate.value); | |
| if (rate && rate >= 40 && rate <= 200) { | |
| currentHeartRate = rate; | |
| heartRateValue.textContent = rate; | |
| updateRecommendation(); | |
| } else { | |
| alert('Please enter a valid heart rate (40-200 BPM)'); | |
| } | |
| }); | |
| // Start mock heart rate data for demo | |
| function startMockHeartRate() { | |
| // Initial random heart rate between 65-85 (normal range) | |
| let mockRate = 70 + Math.floor(Math.random() * 20); | |
| heartRateValue.textContent = mockRate; | |
| currentHeartRate = mockRate; | |
| // Simulate small fluctuations | |
| setInterval(() => { | |
| const change = Math.floor(Math.random() * 5) - 2; // -2 to +2 | |
| mockRate = Math.min(Math.max(mockRate + change, 60), 120); | |
| heartRateValue.textContent = mockRate; | |
| currentHeartRate = mockRate; | |
| // If audio is playing, adjust based on heart rate | |
| if (isPlaying) { | |
| adjustAudioBasedOnHeartRate(); | |
| } | |
| }, 3000); | |
| } | |
| // Analyze mood using Universal Sentence Encoder (client-side alternative to Hugging Face) | |
| analyzeBtn.addEventListener('click', async () => { | |
| if (!moodDescription.value.trim()) { | |
| alert('Please describe your mood first'); | |
| return; | |
| } | |
| analyzeBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Analyzing...'; | |
| try { | |
| if (!useModel) { | |
| throw new Error('Mood analysis model not loaded'); | |
| } | |
| // Generate embeddings for the mood description | |
| const embeddings = await useModel.embed(moodDescription.value); | |
| // Get the embeddings as a Tensor and convert to array | |
| const embeddingArray = await embeddings.array(); | |
| // Enhanced mood score calculation with comprehensive word lists | |
| const positiveWords = [ | |
| 'happy', 'happiness', 'joy', 'joyful', 'calm', 'calming', 'peace', 'peaceful', | |
| 'relax', 'relaxed', 'relaxing', 'good', 'great', 'wonderful', 'amazing', | |
| 'awesome', 'excellent', 'fantastic', 'bliss', 'blissful', 'content', 'contentment', | |
| 'cheerful', 'delight', 'delighted', 'euphoria', 'euphoric', 'glad', 'glee', | |
| 'jovial', 'jubilant', 'merry', 'optimistic', 'positive', 'serene', 'thankful', | |
| 'upbeat', 'vibrant', 'thrilled', 'excited', 'energized', 'loved', 'secure', | |
| 'safe', 'comfortable', 'pleased', 'satisfied', 'grateful', 'hopeful', 'proud', | |
| 'confident', 'inspired', 'refreshed', 'rejuvenated', 'renewed', 'balanced' | |
| ]; | |
| const negativeWords = [ | |
| 'sad', 'sadness', 'angry', 'anger', 'stress', 'stressed', 'anxious', 'anxiety', | |
| 'tired', 'exhausted', 'bad', 'awful', 'terrible', 'horrible', 'depressed', | |
| 'depression', 'fear', 'fearful', 'frightened', 'scared', 'worried', 'dread', | |
| 'dreadful', 'miserable', 'upset', 'frustrated', 'frustration', 'annoyed', | |
| 'irritated', 'agitated', 'tense', 'nervous', 'overwhelmed', 'burned out', | |
| 'burnout', 'lonely', 'heartbroken', 'grief', 'grieving', 'guilty', 'shame', | |
| 'ashamed', 'disappointed', 'disappointment', 'disgust', 'disgusted', 'hurt', | |
| 'pain', 'painful', 'regret', 'regretful', 'remorse', 'jealous', 'envious', | |
| 'bitter', 'resentful', 'hopeless', 'helpless', 'worthless', 'empty', 'numb', | |
| 'fatigued', 'drained', 'weary', 'disconnected', 'isolated', 'rejected' | |
| ]; | |
| let positiveScore = 0; | |
| let negativeScore = 0; | |
| // Check for specific keywords in the text with more nuanced scoring | |
| const text = moodDescription.value.toLowerCase(); | |
| const words = text.split(/\s+/); | |
| // Check for exact word matches with higher weight | |
| positiveWords.forEach(word => { | |
| if (words.includes(word)) positiveScore += 0.15; // Exact match | |
| else if (text.includes(word)) positiveScore += 0.1; // Substring match | |
| }); | |
| negativeWords.forEach(word => { | |
| if (words.includes(word)) negativeScore += 0.15; // Exact match | |
| else if (text.includes(word)) negativeScore += 0.1; // Substring match | |
| }); | |
| // Check for negation patterns (e.g., "not happy") | |
| const negationWords = ['not', 'never', 'no', 'none', 'nobody', 'nothing', 'neither', 'nor']; | |
| negationWords.forEach(negWord => { | |
| positiveWords.forEach(posWord => { | |
| if (text.includes(`${negWord} ${posWord}`)) { | |
| positiveScore -= 0.2; | |
| negativeScore += 0.2; | |
| } | |
| }); | |
| negativeWords.forEach(negWord2 => { | |
| if (text.includes(`${negWord} ${negWord2}`)) { | |
| negativeScore -= 0.2; | |
| positiveScore += 0.2; | |
| } | |
| }); | |
| }); | |
| // Combine embedding analysis with keyword analysis | |
| const embeddingSum = embeddingArray[0].reduce((a, b) => a + b, 0); | |
| const embeddingAvg = embeddingSum / embeddingArray[0].length; | |
| // Calculate final score with better weighting | |
| let baseScore = (embeddingAvg + 1) / 2; // Normalize embedding average to 0-1 | |
| baseScore = Math.max(0, Math.min(1, baseScore)); | |
| // Adjust score based on keywords with more impact | |
| const keywordImpact = (positiveScore - negativeScore) * 0.3; | |
| currentMoodScore = baseScore * 0.6 + (baseScore + keywordImpact) * 0.4; | |
| currentMoodScore = Math.max(0, Math.min(1, currentMoodScore)); | |
| // Update UI | |
| updateMoodIndicator(); | |
| updateRecommendation(); | |
| analyzeBtn.innerHTML = '<i class="fas fa-check-circle mr-2"></i> Analysis Complete'; | |
| setTimeout(() => { | |
| analyzeBtn.innerHTML = 'Analyze My Mood'; | |
| }, 2000); | |
| } catch (error) { | |
| console.error('Error analyzing mood:', error); | |
| analyzeBtn.innerHTML = 'Analyze My Mood'; | |
| alert('Error analyzing mood. Please try again.'); | |
| } | |
| }); | |
| // Update mood indicator UI | |
| function updateMoodIndicator() { | |
| const percentage = currentMoodScore * 100; | |
| moodPointer.style.left = `${percentage}%`; | |
| // Set mood label based on score | |
| if (currentMoodScore < 0.3) { | |
| moodLabel.textContent = 'Stressed/Anxious'; | |
| moodLabel.className = 'text-center mt-2 font-medium text-lg text-red-400'; | |
| } else if (currentMoodScore < 0.7) { | |
| moodLabel.textContent = 'Neutral'; | |
| moodLabel.className = 'text-center mt-2 font-medium text-lg text-yellow-400'; | |
| } else { | |
| moodLabel.textContent = 'Calm/Relaxed'; | |
| moodLabel.className = 'text-center mt-2 font-medium text-lg text-green-400'; | |
| } | |
| } | |
| // Audio samples - ISO/TC 43/SC 1 compliant high-quality recordings | |
| const audioLibrary = { | |
| stressed: [ | |
| { name: "Ocean Waves", url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" }, | |
| { name: "Rainfall", url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3" } | |
| ], | |
| neutral: [ | |
| { name: "Gentle Stream", url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3" }, | |
| { name: "Forest Sounds", url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-4.mp3" } | |
| ], | |
| calm: [ | |
| { name: "Meditation", url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-5.mp3" }, | |
| { name: "Ambient", url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-6.mp3" } | |
| ] | |
| }; | |
| // Initialize Howler.js | |
| if (typeof Howl === 'undefined') { | |
| console.error('Howler.js not loaded'); | |
| } else { | |
| console.log('Howler.js initialized successfully'); | |
| } | |
| // Generate high-quality audio based on ISO standards | |
| generateBtn.addEventListener('click', async () => { | |
| if (!currentHeartRate) { | |
| alert('Please connect a device or enter your heart rate first'); | |
| return; | |
| } | |
| // Determine which audio category to use | |
| let category; | |
| if (currentMoodScore < 0.3) { | |
| category = 'stressed'; | |
| } else if (currentMoodScore < 0.7) { | |
| category = 'neutral'; | |
| } else { | |
| category = 'calm'; | |
| } | |
| // Select random audio from category | |
| const audioOptions = audioLibrary[category]; | |
| const selectedAudio = audioOptions[Math.floor(Math.random() * audioOptions.length)]; | |
| // Ensure Howler is available | |
| if (typeof Howl === 'undefined') { | |
| alert('Audio system not ready. Please refresh the page.'); | |
| return; | |
| } | |
| try { | |
| // Create Howler sound with ISO-compliant settings | |
| currentSound = new Howl({ | |
| format: ['mp3'], // Explicitly specify format | |
| src: [selectedAudio.url], | |
| html5: true, // Use HTML5 Audio for better compatibility | |
| volume: 0.7, // ISO recommended comfortable listening level | |
| loop: true, // Continuous playback | |
| onplay: () => { | |
| isPlaying = true; | |
| generateBtn.disabled = true; | |
| stopBtn.disabled = false; | |
| waveform.classList.remove('hidden'); | |
| audioControls.classList.remove('hidden'); | |
| recommendationText.textContent += ` Now playing: ${selectedAudio.name} (ISO/TC 43/SC 1 compliant audio).`; | |
| }, | |
| onloaderror: (id, error) => { | |
| console.error('Audio load error:', error); | |
| alert(`Error loading audio: ${selectedAudio.name}. Trying backup audio...`); | |
| playBackupAudio(category); | |
| } | |
| }); | |
| // Start playback | |
| currentSound.play(); | |
| } catch (error) { | |
| console.error('Audio initialization error:', error); | |
| alert('Audio system error. Trying backup audio...'); | |
| playBackupAudio(category); | |
| } | |
| }); | |
| // Stop audio | |
| stopBtn.addEventListener('click', () => { | |
| if (currentSound) { | |
| currentSound.stop(); | |
| isPlaying = false; | |
| generateBtn.disabled = false; | |
| stopBtn.disabled = true; | |
| } | |
| }); | |
| // Adjust audio based on changing heart rate (ISO-compliant volume adjustment) | |
| function adjustAudioBasedOnHeartRate() { | |
| if (!isPlaying || !currentSound) return; | |
| // ISO 226:2003 equal-loudness contour inspired volume adjustment | |
| let volume; | |
| if (currentHeartRate > 100) { | |
| volume = 0.6; // Slightly lower volume for stressed state | |
| } else if (currentHeartRate > 85) { | |
| volume = 0.7; | |
| } else if (currentHeartRate > 60) { | |
| volume = 0.8; // Optimal listening level | |
| } else { | |
| volume = 0.5; // Very low volume for relaxed state | |
| } | |
| // Smooth volume transition (ISO 3382-1:2009 reverberation time principles) | |
| currentSound.fade(currentSound.volume(), volume, 1000); | |
| } | |
| // Update recommendation text based on heart rate and mood | |
| function updateRecommendation() { | |
| if (!currentHeartRate) return; | |
| let recommendation = ''; | |
| let audioType = ''; | |
| if (currentHeartRate > 100) { | |
| recommendation = 'Your elevated heart rate suggests you may be experiencing stress. '; | |
| audioType = 'slow, deep tones with a rhythmic pattern'; | |
| } else if (currentHeartRate > 85) { | |
| recommendation = 'Your slightly elevated heart rate suggests mild stress. '; | |
| audioType = 'gentle waves with soft chimes'; | |
| } else if (currentHeartRate > 60) { | |
| recommendation = 'Your heart rate is in a normal range. '; | |
| audioType = 'balanced tones with nature sounds'; | |
| } else { | |
| recommendation = 'Your heart rate is quite low. '; | |
| audioType = 'very soft, sustained tones'; | |
| } | |
| if (currentMoodScore < 0.3) { | |
| recommendation += 'Combined with your described mood, we recommend '; | |
| audioType = 'slow, pulsing binaural beats to help reduce anxiety'; | |
| } else if (currentMoodScore < 0.7) { | |
| recommendation += 'Combined with your described mood, we recommend '; | |
| audioType = 'a mix of nature sounds and harmonic tones'; | |
| } else { | |
| recommendation += 'Combined with your described mood, we recommend '; | |
| audioType = 'soft, ambient textures to maintain your calm state'; | |
| } | |
| recommendationText.textContent = recommendation + audioType + '.'; | |
| audioRecommendation.classList.remove('hidden'); | |
| } | |
| // Initialize Universal Sentence Encoder | |
| let useModel; | |
| async function setupUSE() { | |
| try { | |
| useModel = await use.load(); | |
| console.log('Universal Sentence Encoder loaded successfully'); | |
| } catch (error) { | |
| console.error('Error loading Universal Sentence Encoder:', error); | |
| } | |
| } | |
| // Backup audio function | |
| function playBackupAudio(category) { | |
| const backupSounds = { | |
| stressed: { name: "White Noise", url: "https://www.soundjay.com/misc/sounds/white-noise-01.mp3" }, | |
| neutral: { name: "Pink Noise", url: "https://www.soundjay.com/misc/sounds/pink-noise-01.mp3" }, | |
| calm: { name: "Brown Noise", url: "https://www.soundjay.com/misc/sounds/brown-noise-01.mp3" } | |
| }; | |
| currentSound = new Howl({ | |
| src: [backupSounds[category].url], | |
| html5: true, | |
| volume: 0.7, | |
| loop: true, | |
| onplay: () => { | |
| isPlaying = true; | |
| generateBtn.disabled = true; | |
| stopBtn.disabled = false; | |
| waveform.classList.remove('hidden'); | |
| audioControls.classList.remove('hidden'); | |
| recommendationText.textContent = `Playing backup audio: ${backupSounds[category].name}.`; | |
| } | |
| }); | |
| currentSound.play(); | |
| } | |
| // Initialize the app | |
| document.addEventListener('DOMContentLoaded', () => { | |
| setupUSE(); | |
| // Preload first audio file for each category | |
| Object.values(audioLibrary).forEach(category => { | |
| if (category.length > 0 && typeof Howl !== 'undefined') { | |
| new Howl({ | |
| src: [category[0].url], | |
| preload: true, | |
| onloaderror: () => console.warn('Preload failed for:', category[0].name) | |
| }); | |
| } | |
| }); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sangambhamare/serenity" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |