mindful-voice / index.html
Bakingbad's picture
Add 2 files
05c3db8 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindful Voice - Mental Wellbeing Platform</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">
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Poppins', sans-serif;
transition: background-color 0.3s, color 0.3s;
}
.waveform {
background: linear-gradient(90deg, rgba(99,102,241,0.2) 0%, rgba(168,85,247,0.2) 100%);
height: 100px;
position: relative;
overflow: hidden;
}
.waveform::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 50 Q 25 25 50 50 T 100 50 T 150 50 T 200 50' stroke='%236363f5' fill='none' stroke-width='2'/%3E%3C/svg%3E") repeat-x;
background-size: 200px 100px;
animation: wave 5s linear infinite;
opacity: 0.7;
}
@keyframes wave {
0% { background-position-x: 0; }
100% { background-position-x: 200px; }
}
.brainwave-indicator {
height: 4px;
border-radius: 2px;
background: linear-gradient(90deg, #6366f1, #8b5cf6);
position: relative;
overflow: hidden;
}
.brainwave-indicator::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, rgba(255,255,255,0.1), rgba(255,255,255,0.4), rgba(255,255,255,0.1));
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.toast {
position: fixed;
bottom: 20px;
right: 20px;
transform: translateY(100px);
opacity: 0;
transition: all 0.3s ease;
z-index: 1000;
}
.toast.show {
transform: translateY(0);
opacity: 1;
}
.frequency-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #8b5cf6;
cursor: pointer;
}
.frequency-slider::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: #8b5cf6;
cursor: pointer;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.affirmation-card {
transition: transform 0.2s, box-shadow 0.2s;
}
.affirmation-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.recording-indicator {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
</style>
</head>
<body class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 min-h-screen">
<!-- Toast Notification -->
<div id="toast" class="toast bg-gray-800 text-white px-6 py-3 rounded-lg shadow-lg">
<div class="flex items-center">
<i class="fas fa-check-circle mr-2 text-green-400"></i>
<span id="toast-message">Action completed successfully</span>
</div>
</div>
<!-- Main Container -->
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<header class="flex justify-between items-center mb-8">
<div class="flex items-center">
<i class="fas fa-brain text-3xl mr-3 text-indigo-500"></i>
<h1 class="text-2xl font-bold">Mindful Voice</h1>
</div>
<div class="flex items-center space-x-4">
<button id="theme-toggle" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700">
<i class="fas fa-moon dark:hidden"></i>
<i class="fas fa-sun hidden dark:block"></i>
</button>
<button class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg">
<i class="fas fa-user mr-2"></i>Sign In
</button>
</div>
</header>
<!-- Main Content -->
<main>
<!-- Tabs Navigation -->
<div class="flex border-b border-gray-200 dark:border-gray-700 mb-6">
<button class="tab-btn px-4 py-2 font-medium border-b-2 border-transparent hover:text-indigo-600 dark:hover:text-indigo-400" data-tab="affirmations">
<i class="fas fa-comment-dots mr-2"></i>Affirmations
</button>
<button class="tab-btn px-4 py-2 font-medium border-b-2 border-transparent hover:text-indigo-600 dark:hover:text-indigo-400" data-tab="mixes">
<i class="fas fa-music mr-2"></i>Mixes
</button>
<button class="tab-btn px-4 py-2 font-medium border-b-2 border-transparent hover:text-indigo-600 dark:hover:text-indigo-400" data-tab="binaural">
<i class="fas fa-wave-square mr-2"></i>Binaural Beats
</button>
</div>
<!-- Tab Contents -->
<div class="tab-contents">
<!-- Affirmations Tab -->
<div id="affirmations" class="tab-content active">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-semibold">Your Affirmations</h2>
<button id="add-affirmation-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg">
<i class="fas fa-plus mr-2"></i>Add Affirmation
</button>
</div>
<!-- Categories Filter -->
<div class="mb-6">
<div class="flex flex-wrap gap-2">
<button class="category-filter px-3 py-1 rounded-full text-sm bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200" data-category="all">All</button>
<button class="category-filter px-3 py-1 rounded-full text-sm bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200" data-category="confidence">Confidence</button>
<button class="category-filter px-3 py-1 rounded-full text-sm bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200" data-category="health">Health</button>
<button class="category-filter px-3 py-1 rounded-full text-sm bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200" data-category="wealth">Wealth</button>
<button class="category-filter px-3 py-1 rounded-full text-sm bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200" data-category="love">Love</button>
</div>
</div>
<!-- Affirmations Grid -->
<div id="affirmations-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Affirmation cards will be added here by JavaScript -->
</div>
<!-- Add Affirmation Modal -->
<div id="add-affirmation-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-md">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">Add New Affirmation</h3>
<button id="close-affirmation-modal" class="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
<i class="fas fa-times"></i>
</button>
</div>
<form id="affirmation-form">
<div class="mb-4">
<label for="affirmation-text" class="block text-sm font-medium mb-1">Affirmation Text</label>
<textarea id="affirmation-text" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700" rows="3" placeholder="I am confident and capable..."></textarea>
</div>
<div class="mb-4">
<label for="affirmation-category" class="block text-sm font-medium mb-1">Category</label>
<select id="affirmation-category" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700">
<option value="confidence">Confidence</option>
<option value="health">Health</option>
<option value="wealth">Wealth</option>
<option value="love">Love</option>
<option value="other">Other</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Record Audio</label>
<div class="flex items-center space-x-4">
<button id="record-btn" type="button" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg">
<i class="fas fa-microphone mr-2"></i>Record
</button>
<div id="recording-indicator" class="recording-indicator hidden items-center text-red-500">
<div class="w-3 h-3 rounded-full bg-red-500 mr-2"></div>
<span>Recording...</span>
</div>
</div>
</div>
<div class="flex justify-end space-x-3">
<button type="button" id="cancel-affirmation" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">Cancel</button>
<button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg">Save</button>
</div>
</form>
</div>
</div>
</div>
<!-- Mixes Tab -->
<div id="mixes" class="tab-content">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-semibold">Your Mixes</h2>
<button id="add-mix-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg">
<i class="fas fa-plus mr-2"></i>Create Mix
</button>
</div>
<div id="mixes-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Mix cards will be added here by JavaScript -->
</div>
<!-- Add Mix Modal -->
<div id="add-mix-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-2xl">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">Create New Mix</h3>
<button id="close-mix-modal" class="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
<i class="fas fa-times"></i>
</button>
</div>
<form id="mix-form">
<div class="mb-4">
<label for="mix-name" class="block text-sm font-medium mb-1">Mix Name</label>
<input type="text" id="mix-name" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700" placeholder="Morning Motivation">
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Select Affirmations</label>
<div class="max-h-48 overflow-y-auto border border-gray-300 dark:border-gray-600 rounded-lg p-2 bg-white dark:bg-gray-700">
<!-- Affirmation checkboxes will be added here by JavaScript -->
<div id="mix-affirmations-list" class="space-y-2"></div>
</div>
</div>
<div class="mb-4">
<label for="mix-interval" class="block text-sm font-medium mb-1">Interval Between Affirmations (seconds)</label>
<input type="number" id="mix-interval" min="1" max="60" value="5" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700">
</div>
<div class="mb-4">
<label for="mix-background" class="block text-sm font-medium mb-1">Background Sound</label>
<select id="mix-background" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700">
<option value="none">None</option>
<option value="rain">Rain</option>
<option value="ocean">Ocean Waves</option>
<option value="forest">Forest</option>
<option value="white-noise">White Noise</option>
<option value="brown-noise">Brown Noise</option>
</select>
</div>
<div class="flex justify-end space-x-3">
<button type="button" id="cancel-mix" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">Cancel</button>
<button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg">Create Mix</button>
</div>
</form>
</div>
</div>
</div>
<!-- Binaural Beats Tab -->
<div id="binaural" class="tab-content">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-semibold">Binaural Beats Generator</h2>
<button id="save-beat-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg">
<i class="fas fa-save mr-2"></i>Save Beat
</button>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-2">
<!-- Waveform Visualization -->
<div class="waveform rounded-lg mb-6">
<div class="absolute inset-0 flex items-center justify-center">
<span id="current-frequency" class="text-2xl font-bold text-white">0 Hz</span>
</div>
</div>
<!-- Brainwave State Info -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 mb-6">
<div class="flex justify-between items-center mb-2">
<h3 class="font-medium">Current Brainwave State</h3>
<span id="brainwave-state" class="px-2 py-1 rounded-full text-xs font-medium bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200">None</span>
</div>
<div class="brainwave-indicator mb-2"></div>
<p id="brainwave-description" class="text-sm text-gray-600 dark:text-gray-400">No frequency selected</p>
</div>
<!-- Presets -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 mb-6">
<h3 class="font-medium mb-3">Preset Frequencies</h3>
<div class="grid grid-cols-2 md:grid-cols-5 gap-2">
<button class="preset-btn px-3 py-2 rounded-lg text-sm bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600" data-frequency="3" data-state="delta">
<div class="font-medium">Delta</div>
<div class="text-xs text-gray-500 dark:text-gray-400">0.5-4 Hz</div>
</button>
<button class="preset-btn px-3 py-2 rounded-lg text-sm bg-blue-100 dark:bg-blue-900 hover:bg-blue-200 dark:hover:bg-blue-800" data-frequency="6" data-state="theta">
<div class="font-medium">Theta</div>
<div class="text-xs text-gray-500 dark:text-gray-400">4-8 Hz</div>
</button>
<button class="preset-btn px-3 py-2 rounded-lg text-sm bg-green-100 dark:bg-green-900 hover:bg-green-200 dark:hover:bg-green-800" data-frequency="10" data-state="alpha">
<div class="font-medium">Alpha</div>
<div class="text-xs text-gray-500 dark:text-gray-400">8-12 Hz</div>
</button>
<button class="preset-btn px-3 py-2 rounded-lg text-sm bg-yellow-100 dark:bg-yellow-900 hover:bg-yellow-200 dark:hover:bg-yellow-800" data-frequency="18" data-state="beta">
<div class="font-medium">Beta</div>
<div class="text-xs text-gray-500 dark:text-gray-400">12-30 Hz</div>
</button>
<button class="preset-btn px-3 py-2 rounded-lg text-sm bg-purple-100 dark:bg-purple-900 hover:bg-purple-200 dark:hover:bg-purple-800" data-frequency="40" data-state="gamma">
<div class="font-medium">Gamma</div>
<div class="text-xs text-gray-500 dark:text-gray-400">30-100 Hz</div>
</button>
</div>
</div>
</div>
<div>
<!-- Controls -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 mb-6">
<div class="flex justify-between items-center mb-4">
<h3 class="font-medium">Controls</h3>
<div class="flex space-x-2">
<button id="play-beat-btn" class="bg-green-500 hover:bg-green-600 text-white p-2 rounded-full">
<i class="fas fa-play"></i>
</button>
<button id="stop-beat-btn" class="bg-red-500 hover:bg-red-600 text-white p-2 rounded-full">
<i class="fas fa-stop"></i>
</button>
</div>
</div>
<div class="mb-4">
<label for="base-frequency" class="block text-sm font-medium mb-1">Base Frequency (Hz)</label>
<input type="range" id="base-frequency" min="20" max="500" value="200" class="w-full frequency-slider">
<div class="flex justify-between text-xs text-gray-500 dark:text-gray-400">
<span>20 Hz</span>
<span id="base-frequency-value">200 Hz</span>
<span>500 Hz</span>
</div>
</div>
<div class="mb-4">
<label for="beat-frequency" class="block text-sm font-medium mb-1">Beat Frequency (Hz)</label>
<input type="range" id="beat-frequency" min="0.5" max="30" value="10" step="0.5" class="w-full frequency-slider">
<div class="flex justify-between text-xs text-gray-500 dark:text-gray-400">
<span>0.5 Hz</span>
<span id="beat-frequency-value">10 Hz</span>
<span>30 Hz</span>
</div>
</div>
<div class="mb-4">
<label for="waveform" class="block text-sm font-medium mb-1">Waveform</label>
<select id="waveform" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700">
<option value="sine">Sine</option>
<option value="square">Square</option>
<option value="sawtooth">Sawtooth</option>
<option value="triangle">Triangle</option>
</select>
</div>
<div class="mb-4">
<label for="volume" class="block text-sm font-medium mb-1">Volume</label>
<input type="range" id="volume" min="0" max="1" value="0.5" step="0.01" class="w-full">
<div class="flex justify-between text-xs text-gray-500 dark:text-gray-400">
<span>0%</span>
<span id="volume-value">50%</span>
<span>100%</span>
</div>
</div>
<div class="mb-4">
<label for="balance" class="block text-sm font-medium mb-1">Balance (L/R)</label>
<input type="range" id="balance" min="-1" max="1" value="0" step="0.1" class="w-full">
<div class="flex justify-between text-xs text-gray-500 dark:text-gray-400">
<span>Left</span>
<span>Center</span>
<span>Right</span>
</div>
</div>
<div class="mb-4">
<label for="duration" class="block text-sm font-medium mb-1">Duration (minutes)</label>
<input type="number" id="duration" min="1" max="120" value="15" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700">
</div>
<div class="flex items-center">
<input type="checkbox" id="ambient-noise" class="mr-2">
<label for="ambient-noise" class="text-sm">Add Ambient Noise</label>
</div>
</div>
<!-- Saved Beats -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4">
<h3 class="font-medium mb-3">Your Saved Beats</h3>
<div id="saved-beats-list" class="space-y-2">
<!-- Saved beats will be added here by JavaScript -->
<div class="text-center py-4 text-gray-500 dark:text-gray-400">
<i class="fas fa-music text-2xl mb-2"></i>
<p>No saved beats yet</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<script>
// Theme Toggle
const themeToggle = document.getElementById('theme-toggle');
const html = document.documentElement;
if (localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
html.classList.add('dark');
} else {
html.classList.remove('dark');
}
themeToggle.addEventListener('click', () => {
html.classList.toggle('dark');
localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
});
// Tab Navigation
const tabButtons = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('.tab-content');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const tabId = button.getAttribute('data-tab');
// Update active tab button
tabButtons.forEach(btn => {
btn.classList.remove('border-indigo-600', 'text-indigo-600', 'dark:border-indigo-400', 'dark:text-indigo-400');
});
button.classList.add('border-indigo-600', 'text-indigo-600', 'dark:border-indigo-400', 'dark:text-indigo-400');
// Show selected tab content
tabContents.forEach(content => {
content.classList.remove('active');
});
document.getElementById(tabId).classList.add('active');
});
});
// Set first tab as active by default
if (tabButtons.length > 0) {
tabButtons[0].classList.add('border-indigo-600', 'text-indigo-600', 'dark:border-indigo-400', 'dark:text-indigo-400');
}
// Toast Notification
function showToast(message, type = 'success') {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toast-message');
toastMessage.textContent = message;
// Update icon based on type
const icon = toast.querySelector('i');
icon.className = type === 'success' ? 'fas fa-check-circle mr-2 text-green-400' : 'fas fa-exclamation-circle mr-2 text-yellow-400';
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
// Sample data for affirmations
let affirmations = [
{ id: 1, text: "I am confident and capable in everything I do", category: "confidence", audio: null },
{ id: 2, text: "My body is healthy and full of energy", category: "health", audio: null },
{ id: 3, text: "Abundance flows freely to me", category: "wealth", audio: null },
{ id: 4, text: "I am worthy of love and respect", category: "love", audio: null }
];
// Sample data for mixes
let mixes = [
{ id: 1, name: "Morning Motivation", affirmations: [1, 2], interval: 5, background: "rain" },
{ id: 2, name: "Evening Relaxation", affirmations: [3, 4], interval: 8, background: "ocean" }
];
// Sample data for saved beats
let savedBeats = [
{ id: 1, name: "Deep Meditation", baseFreq: 200, beatFreq: 6, waveform: "sine", duration: 20 },
{ id: 2, name: "Focus Boost", baseFreq: 300, beatFreq: 18, waveform: "square", duration: 30 }
];
// Render affirmations
function renderAffirmations(filterCategory = 'all') {
const grid = document.getElementById('affirmations-grid');
grid.innerHTML = '';
const filtered = filterCategory === 'all'
? affirmations
: affirmations.filter(a => a.category === filterCategory);
if (filtered.length === 0) {
grid.innerHTML = `
<div class="col-span-full text-center py-8 text-gray-500 dark:text-gray-400">
<i class="fas fa-comment-slash text-3xl mb-2"></i>
<p>No affirmations found</p>
</div>
`;
return;
}
filtered.forEach(affirmation => {
const card = document.createElement('div');
card.className = `affirmation-card bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden border-l-4 ${
affirmation.category === 'confidence' ? 'border-indigo-500' :
affirmation.category === 'health' ? 'border-green-500' :
affirmation.category === 'wealth' ? 'border-purple-500' :
'border-yellow-500'
}`;
card.innerHTML = `
<div class="p-4">
<div class="flex justify-between items-start mb-2">
<span class="px-2 py-1 rounded-full text-xs font-medium ${
affirmation.category === 'confidence' ? 'bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200' :
affirmation.category === 'health' ? 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200' :
affirmation.category === 'wealth' ? 'bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200' :
'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200'
}">${affirmation.category.charAt(0).toUpperCase() + affirmation.category.slice(1)}</span>
<div class="flex space-x-2">
<button class="edit-affirmation-btn text-gray-500 hover:text-indigo-500 dark:hover:text-indigo-400" data-id="${affirmation.id}">
<i class="fas fa-edit"></i>
</button>
<button class="delete-affirmation-btn text-gray-500 hover:text-red-500 dark:hover:text-red-400" data-id="${affirmation.id}">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<p class="text-gray-700 dark:text-gray-300 mb-4">${affirmation.text}</p>
<div class="flex items-center justify-between">
<div class="flex items-center space-x-2">
<button class="play-affirmation-btn bg-indigo-600 hover:bg-indigo-700 text-white px-3 py-1 rounded-lg text-sm" data-id="${affirmation.id}">
<i class="fas fa-play mr-1"></i>Play
</button>
<button class="loop-affirmation-btn bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 px-3 py-1 rounded-lg text-sm" data-id="${affirmation.id}">
<i class="fas fa-redo mr-1"></i>Loop
</button>
</div>
<span class="text-xs text-gray-500 dark:text-gray-400">ID: ${affirmation.id}</span>
</div>
</div>
`;
grid.appendChild(card);
});
// Add event listeners to buttons
document.querySelectorAll('.play-affirmation-btn').forEach(btn => {
btn.addEventListener('click', () => playAffirmation(btn.getAttribute('data-id')));
});
document.querySelectorAll('.loop-affirmation-btn').forEach(btn => {
btn.addEventListener('click', () => loopAffirmation(btn.getAttribute('data-id')));
});
document.querySelectorAll('.edit-affirmation-btn').forEach(btn => {
btn.addEventListener('click', () => editAffirmation(btn.getAttribute('data-id')));
});
document.querySelectorAll('.delete-affirmation-btn').forEach(btn => {
btn.addEventListener('click', () => deleteAffirmation(btn.getAttribute('data-id')));
});
}
// Play affirmation
function playAffirmation(id) {
const affirmation = affirmations.find(a => a.id == id);
showToast(`Playing affirmation: "${affirmation.text.substring(0, 20)}..."`);
// In a real app, this would play the audio
}
// Loop affirmation
function loopAffirmation(id) {
const affirmation = affirmations.find(a => a.id == id);
showToast(`Looping affirmation: "${affirmation.text.substring(0, 20)}..."`);
// In a real app, this would loop the audio
}
// Edit affirmation
function editAffirmation(id) {
const affirmation = affirmations.find(a => a.id == id);
document.getElementById('affirmation-text').value = affirmation.text;
document.getElementById('affirmation-category').value = affirmation.category;
// Show the modal
document.getElementById('add-affirmation-modal').classList.remove('hidden');
// Change the form to update mode
const form = document.getElementById('affirmation-form');
form.setAttribute('data-mode', 'edit');
form.setAttribute('data-id', id);
}
// Delete affirmation
function deleteAffirmation(id) {
if (confirm('Are you sure you want to delete this affirmation?')) {
affirmations = affirmations.filter(a => a.id != id);
renderAffirmations();
showToast('Affirmation deleted successfully');
}
}
// Category filter
document.querySelectorAll('.category-filter').forEach(btn => {
btn.addEventListener('click', () => {
const category = btn.getAttribute('data-category');
renderAffirmations(category);
// Update active filter button
document.querySelectorAll('.category-filter').forEach(b => {
b.classList.remove('font-semibold', 'scale-105');
});
btn.classList.add('font-semibold', 'scale-105');
});
});
// Add affirmation modal
document.getElementById('add-affirmation-btn').addEventListener('click', () => {
// Reset form
document.getElementById('affirmation-form').reset();
document.getElementById('affirmation-form').setAttribute('data-mode', 'add');
document.getElementById('add-affirmation-modal').classList.remove('hidden');
});
// Close affirmation modal
document.getElementById('close-affirmation-modal').addEventListener('click', () => {
document.getElementById('add-affirmation-modal').classList.add('hidden');
});
document.getElementById('cancel-affirmation').addEventListener('click', () => {
document.getElementById('add-affirmation-modal').classList.add('hidden');
});
// Record button
document.getElementById('record-btn').addEventListener('click', () => {
const recordingIndicator = document.getElementById('recording-indicator');
if (recordingIndicator.classList.contains('hidden')) {
// Start recording
recordingIndicator.classList.remove('hidden');
document.getElementById('record-btn').innerHTML = '<i class="fas fa-stop mr-2"></i>Stop';
showToast('Recording started');
} else {
// Stop recording
recordingIndicator.classList.add('hidden');
document.getElementById('record-btn').innerHTML = '<i class="fas fa-microphone mr-2"></i>Record';
showToast('Recording saved');
}
});
// Affirmation form submission
document.getElementById('affirmation-form').addEventListener('submit', (e) => {
e.preventDefault();
const form = e.target;
const mode = form.getAttribute('data-mode');
const text = document.getElementById('affirmation-text').value;
const category = document.getElementById('affirmation-category').value;
if (mode === 'add') {
// Add new affirmation
const newId = affirmations.length > 0 ? Math.max(...affirmations.map(a => a.id)) + 1 : 1;
affirmations.push({
id: newId,
text,
category,
audio: null
});
showToast('Affirmation added successfully');
} else {
// Update existing affirmation
const id = form.getAttribute('data-id');
const index = affirmations.findIndex(a => a.id == id);
if (index !== -1) {
affirmations[index] = {
...affirmations[index],
text,
category
};
showToast('Affirmation updated successfully');
}
}
renderAffirmations();
document.getElementById('add-affirmation-modal').classList.add('hidden');
});
// Render mixes
function renderMixes() {
const grid = document.getElementById('mixes-grid');
grid.innerHTML = '';
if (mixes.length === 0) {
grid.innerHTML = `
<div class="col-span-full text-center py-8 text-gray-500 dark:text-gray-400">
<i class="fas fa-music text-3xl mb-2"></i>
<p>No mixes created yet</p>
</div>
`;
return;
}
mixes.forEach(mix => {
const card = document.createElement('div');
card.className = 'bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden';
// Get affirmation texts for display
const mixAffirmations = mix.affirmations.map(id => {
const aff = affirmations.find(a => a.id === id);
return aff ? aff.text.substring(0, 20) + (aff.text.length > 20 ? '...' : '') : 'Deleted';
});
card.innerHTML = `
<div class="p-4">
<div class="flex justify-between items-start mb-2">
<h3 class="font-medium">${mix.name}</h3>
<div class="flex space-x-2">
<button class="edit-mix-btn text-gray-500 hover:text-indigo-500 dark:hover:text-indigo-400" data-id="${mix.id}">
<i class="fas fa-edit"></i>
</button>
<button class="delete-mix-btn text-gray-500 hover:text-red-500 dark:hover:text-red-400" data-id="${mix.id}">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="mb-3">
<div class="text-sm text-gray-600 dark:text-gray-300 mb-1">Affirmations:</div>
<ul class="text-xs text-gray-500 dark:text-gray-400 space-y-1">
${mixAffirmations.map(a => `<li class="flex items-center"><i class="fas fa-check-circle mr-2 text-green-500"></i>${a}</li>`).join('')}
</ul>
</div>
<div class="flex flex-wrap gap-2 text-xs mb-3">
<span class="bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">Interval: ${mix.interval}s</span>
<span class="bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded">Background: ${mix.background}</span>
</div>
<button class="play-mix-btn w-full bg-indigo-600 hover:bg-indigo-700 text-white px-3 py-2 rounded-lg text-sm" data-id="${mix.id}">
<i class="fas fa-play mr-1"></i>Play Mix
</button>
</div>
`;
grid.appendChild(card);
});
// Add event listeners to buttons
document.querySelectorAll('.play-mix-btn').forEach(btn => {
btn.addEventListener('click', () => playMix(btn.getAttribute('data-id')));
});
document.querySelectorAll('.edit-mix-btn').forEach(btn => {
btn.addEventListener('click', () => editMix(btn.getAttribute('data-id')));
});
document.querySelectorAll('.delete-mix-btn').forEach(btn => {
btn.addEventListener('click', () => deleteMix(btn.getAttribute('data-id')));
});
}
// Play mix
function playMix(id) {
const mix = mixes.find(m => m.id == id);
showToast(`Playing mix: "${mix.name}"`);
// In a real app, this would play the mix
}
// Edit mix
function editMix(id) {
const mix = mixes.find(m => m.id == id);
document.getElementById('mix-name').value = mix.name;
document.getElementById('mix-interval').value = mix.interval;
document.getElementById('mix-background').value = mix.background;
// Populate affirmations list
const affirmationsList = document.getElementById('mix-affirmations-list');
affirmationsList.innerHTML = '';
affirmations.forEach(affirmation => {
const div = document.createElement('div');
div.className = 'flex items-center';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `affirmation-${affirmation.id}`;
checkbox.value = affirmation.id;
checkbox.className = 'mr-2';
if (mix.affirmations.includes(affirmation.id)) {
checkbox.checked = true;
}
const label = document.createElement('label');
label.htmlFor = `affirmation-${affirmation.id}`;
label.className = 'text-sm';
label.textContent = affirmation.text.substring(0, 30) + (affirmation.text.length > 30 ? '...' : '');
div.appendChild(checkbox);
div.appendChild(label);
affirmationsList.appendChild(div);
});
// Show the modal
document.getElementById('add-mix-modal').classList.remove('hidden');
// Change the form to update mode
const form = document.getElementById('mix-form');
form.setAttribute('data-mode', 'edit');
form.setAttribute('data-id', id);
}
// Delete mix
function deleteMix(id) {
if (confirm('Are you sure you want to delete this mix?')) {
mixes = mixes.filter(m => m.id != id);
renderMixes();
showToast('Mix deleted successfully');
}
}
// Add mix modal
document.getElementById('add-mix-btn').addEventListener('click', () => {
// Reset form
document.getElementById('mix-form').reset();
document.getElementById('mix-form').setAttribute('data-mode', 'add');
// Populate affirmations list
const affirmationsList = document.getElementById('mix-affirmations-list');
affirmationsList.innerHTML = '';
affirmations.forEach(affirmation => {
const div = document.createElement('div');
div.className = 'flex items-center';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `affirmation-${affirmation.id}`;
checkbox.value = affirmation.id;
checkbox.className = 'mr-2';
const label = document.createElement('label');
label.htmlFor = `affirmation-${affirmation.id}`;
label.className = 'text-sm';
label.textContent = affirmation.text.substring(0, 30) + (affirmation.text.length > 30 ? '...' : '');
div.appendChild(checkbox);
div.appendChild(label);
affirmationsList.appendChild(div);
});
document.getElementById('add-mix-modal').classList.remove('hidden');
});
// Close mix modal
document.getElementById('close-mix-modal').addEventListener('click', () => {
document.getElementById('add-mix-modal').classList.add('hidden');
});
document.getElementById('cancel-mix').addEventListener('click', () => {
document.getElementById('add-mix-modal').classList.add('hidden');
});
// Mix form submission
document.getElementById('mix-form').addEventListener('submit', (e) => {
e.preventDefault();
const form = e.target;
const mode = form.getAttribute('data-mode');
const name = document.getElementById('mix-name').value;
const interval = parseInt(document.getElementById('mix-interval').value);
const background = document.getElementById('mix-background').value;
// Get selected affirmations
const selectedAffirmations = [];
document.querySelectorAll('#mix-affirmations-list input[type="checkbox"]:checked').forEach(checkbox => {
selectedAffirmations.push(parseInt(checkbox.value));
});
if (mode === 'add') {
// Add new mix
const newId = mixes.length > 0 ? Math.max(...mixes.map(m => m.id)) + 1 : 1;
mixes.push({
id: newId,
name,
affirmations: selectedAffirmations,
interval,
background
});
showToast('Mix created successfully');
} else {
// Update existing mix
const id = form.getAttribute('data-id');
const index = mixes.findIndex(m => m.id == id);
if (index !== -1) {
mixes[index] = {
id,
name,
affirmations: selectedAffirmations,
interval,
background
};
showToast('Mix updated successfully');
}
}
renderMixes();
document.getElementById('add-mix-modal').classList.add('hidden');
});
// Binaural Beats Controls
let audioContext;
let oscillatorLeft;
let oscillatorRight;
let gainNode;
let pannerNode;
let isPlaying = false;
// Initialize audio context on user interaction
function initAudioContext() {
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
gainNode = audioContext.createGain();
gainNode.gain.value = 0.5;
gainNode.connect(audioContext.destination);
pannerNode = audioContext.createStereoPanner();
pannerNode.pan.value = 0;
pannerNode.connect(gainNode);
showToast('Audio context initialized');
}
}
// Play binaural beat
function playBinauralBeat() {
if (isPlaying) return;
initAudioContext();
const baseFrequency = parseFloat(document.getElementById('base-frequency').value);
const beatFrequency = parseFloat(document.getElementById('beat-frequency').value);
const waveform = document.getElementById('waveform').value;
oscillatorLeft = audioContext.createOscillator();
oscillatorRight = audioContext.createOscillator();
oscillatorLeft.type = waveform;
oscillatorRight.type = waveform;
oscillatorLeft.frequency.value = baseFrequency;
oscillatorRight.frequency.value = baseFrequency + beatFrequency;
oscillatorLeft.connect(pannerNode);
oscillatorRight.connect(pannerNode);
oscillatorLeft.start();
oscillatorRight.start();
isPlaying = true;
document.getElementById('play-beat-btn').innerHTML = '<i class="fas fa-pause"></i>';
// Update UI
updateFrequencyDisplay(baseFrequency, beatFrequency);
updateBrainwaveState(beatFrequency);
showToast(`Playing binaural beat at ${beatFrequency}Hz`);
}
// Stop binaural beat
function stopBinauralBeat() {
if (!isPlaying) return;
if (oscillatorLeft) {
oscillatorLeft.stop();
oscillatorLeft = null;
}
if (oscillatorRight) {
oscillatorRight.stop();
oscillatorRight = null;
}
isPlaying = false;
document.getElementById('play-beat-btn').innerHTML = '<i class="fas fa-play"></i>';
// Reset UI
document.getElementById('current-frequency').textContent = '0 Hz';
document.getElementById('brainwave-state').textContent = 'None';
document.getElementById('brainwave-description').textContent = 'No frequency selected';
showToast('Binaural beat stopped');
}
// Toggle play/stop
document.getElementById('play-beat-btn').addEventListener('click', () => {
if (isPlaying) {
stopBinauralBeat();
} else {
playBinauralBeat();
}
});
document.getElementById('stop-beat-btn').addEventListener('click', stopBinauralBeat);
// Update frequency display
function updateFrequencyDisplay(baseFreq, beatFreq) {
document.getElementById('current-frequency').textContent = `${beatFreq} Hz`;
document.getElementById('base-frequency-value').textContent = `${baseFreq} Hz`;
document.getElementById('beat-frequency-value').textContent = `${beatFreq} Hz`;
}
// Update brainwave state
function updateBrainwaveState(frequency) {
let state, description, colorClass;
if (frequency >= 0.5 && frequency < 4) {
state = 'Delta';
description = 'Deep sleep, healing, transcendence';
colorClass = 'bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200';
} else if (frequency >= 4 && frequency < 8) {
state = 'Theta';
description = 'Visualization, trance, dreaming states';
colorClass = 'bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200';
} else if (frequency >= 8 && frequency < 12) {
state = 'Alpha';
description = 'Meditation, creativity, relaxation';
colorClass = 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200';
} else if (frequency >= 12 && frequency < 30) {
state = 'Beta';
description = 'Alertness, concentration, active thinking';
colorClass = 'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200';
} else if (frequency >= 30) {
state = 'Gamma';
description = 'Peak experiences, insight, synchronization';
colorClass = 'bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200';
} else {
state = 'None';
description = 'No frequency selected';
colorClass = 'bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200';
}
const stateElement = document.getElementById('brainwave-state');
stateElement.textContent = state;
stateElement.className = `px-2 py-1 rounded-full text-xs font-medium ${colorClass}`;
document.getElementById('brainwave-description').textContent = description;
}
// Slider controls
document.getElementById('base-frequency').addEventListener('input', (e) => {
const value = e.target.value;
document.getElementById('base-frequency-value').textContent = `${value} Hz`;
if (isPlaying) {
oscillatorLeft.frequency.value = value;
oscillatorRight.frequency.value = value + parseFloat(document.getElementById('beat-frequency').value);
updateFrequencyDisplay(value, parseFloat(document.getElementById('beat-frequency').value));
}
});
document.getElementById('beat-frequency').addEventListener('input', (e) => {
const value = e.target.value;
document.getElementById('beat-frequency-value').textContent = `${value} Hz`;
if (isPlaying) {
oscillatorRight.frequency.value = parseFloat(document.getElementById('base-frequency').value) + value;
updateFrequencyDisplay(parseFloat(document.getElementById('base-frequency').value), value);
updateBrainwaveState(value);
} else {
updateBrainwaveState(value);
}
});
document.getElementById('volume').addEventListener('input', (e) => {
const value = e.target.value;
document.getElementById('volume-value').textContent = `${Math.round(value * 100)}%`;
if (gainNode) {
gainNode.gain.value = value;
}
});
document.getElementById('balance').addEventListener('input', (e) => {
if (pannerNode) {
pannerNode.pan.value = e.target.value;
}
});
// Preset buttons
document.querySelectorAll('.preset-btn').forEach(btn => {
btn.addEventListener('click', () => {
const frequency = btn.getAttribute('data-frequency');
const state = btn.getAttribute('data-state');
document.getElementById('beat-frequency').value = frequency;
document.getElementById('beat-frequency-value').textContent = `${frequency} Hz`;
if (isPlaying) {
oscillatorRight.frequency.value = parseFloat(document.getElementById('base-frequency').value) + parseFloat(frequency);
}
updateBrainwaveState(frequency);
showToast(`Preset loaded: ${state.charAt(0).toUpperCase() + state.slice(1)} waves`);
});
});
// Save beat
document.getElementById('save-beat-btn').addEventListener('click', () => {
const name = prompt('Enter a name for this binaural beat:');
if (!name) return;
const newId = savedBeats.length > 0 ? Math.max(...savedBeats.map(b => b.id)) + 1 : 1;
savedBeats.push({
id: newId,
name,
baseFreq: parseFloat(document.getElementById('base-frequency').value),
beatFreq: parseFloat(document.getElementById('beat-frequency').value),
waveform: document.getElementById('waveform').value,
duration: parseInt(document.getElementById('duration').value)
});
renderSavedBeats();
showToast('Binaural beat saved');
});
// Render saved beats
function renderSavedBeats() {
const list = document.getElementById('saved-beats-list');
if (savedBeats.length === 0) {
list.innerHTML = `
<div class="text-center py-4 text-gray-500 dark:text-gray-400">
<i class="fas fa-music text-2xl mb-2"></i>
<p>No saved beats yet</p>
</div>
`;
return;
}
list.innerHTML = '';
savedBeats.forEach(beat => {
const div = document.createElement('div');
div.className = 'bg-gray-100 dark:bg-gray-700 rounded-lg p-3 flex justify-between items-center';
div.innerHTML = `
<div>
<div class="font-medium">${beat.name}</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
${beat.baseFreq}Hz + ${beat.beatFreq}Hz • ${beat.waveform}${beat.duration}min
</div>
</div>
<div class="flex space-x-2">
<button class="load-beat-btn text-indigo-500 dark:text-indigo-400" data-id="${beat.id}">
<i class="fas fa-play"></i>
</button>
<button class="delete-beat-btn text-gray-500 hover:text-red-500 dark:hover:text-red-400" data-id="${beat.id}">
<i class="fas fa-trash"></i>
</button>
</div>
`;
list.appendChild(div);
});
// Add event listeners
document.querySelectorAll('.load-beat-btn').forEach(btn => {
btn.addEventListener('click', () => loadBeat(btn.getAttribute('data-id')));
});
document.querySelectorAll('.delete-beat-btn').forEach(btn => {
btn.addEventListener('click', () => deleteBeat(btn.getAttribute('data-id')));
});
}
// Load beat
function loadBeat(id) {
const beat = savedBeats.find(b => b.id == id);
document.getElementById('base-frequency').value = beat.baseFreq;
document.getElementById('base-frequency-value').textContent = `${beat.baseFreq} Hz`;
document.getElementById('beat-frequency').value = beat.beatFreq;
document.getElementById('beat-frequency-value').textContent = `${beat.beatFreq} Hz`;
document.getElementById('waveform').value = beat.waveform;
document.getElementById('duration').value = beat.duration;
updateBrainwaveState(beat.beatFreq);
showToast(`Loaded beat: ${beat.name}`);
}
// Delete beat
function deleteBeat(id) {
if (confirm('Are you sure you want to delete this saved beat?')) {
savedBeats = savedBeats.filter(b => b.id != id);
renderSavedBeats();
showToast('Beat deleted');
}
}
// Initialize the app
document.addEventListener('DOMContentLoaded', () => {
renderAffirmations();
renderMixes();
renderSavedBeats();
// Set default values for binaural beats
updateBrainwaveState(10); // Default to Alpha
});
</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=Bakingbad/mindful-voice" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>