Sleep & Circadian Rhythm Optimizer

Sleep & Circadian Rhythm Optimizer

Analyze your sleep patterns to improve your rest and energy.

Log your sleep for a few days to see your analysis here.

${log.bedtime} - ${log.waketime} (${duration} hrs)

${'★'.repeat(log.quality)}${'☆'.repeat(5 - log.quality)}

${log.notes ? `

"${log.notes}"

` : ''}
`; }).join(''); } // --- DASHBOARD & ANALYSIS --- // function updateDashboard() { const recentLogs = sleepLogs.slice(0, 7); if (recentLogs.length < 2) { dashboardPlaceholder.classList.remove('hidden'); dashboardContent.classList.add('hidden'); return; } dashboardPlaceholder.classList.add('hidden'); dashboardContent.classList.remove('hidden'); const score = calculateSleepScore(recentLogs); const recommendations = generateAIRecommendations(recentLogs, score); sleepScoreStat.textContent = score; aiRecommendationsEl.innerHTML = recommendations.map(rec => { let icon = rec.type === 'success' ? '✅' : '⚠️'; return `
${icon}

${rec.text}

`; }).join(''); renderConsistencyChart(recentLogs); } function calculateSleepScore(logs) { // Duration Score (40%) const durations = logs.map(log => { const bedtime = new Date(`${log.date}T${log.bedtime}`); const waketime = new Date(`${log.date}T${log.waketime}`); if (waketime < bedtime) waketime.setDate(waketime.getDate() + 1); return (waketime - bedtime) / 3600000; }); const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length; const durationDiff = Math.abs(avgDuration - settings.targetDuration); const durationScore = Math.max(0, 100 - (durationDiff * 50)); // 1hr diff = 50 score // Consistency Score (40%) const bedtimeMinutes = logs.map(log => { const [h, m] = log.bedtime.split(':').map(Number); return h * 60 + m; }); const avgBedtime = bedtimeMinutes.reduce((a, b) => a + b, 0) / bedtimeMinutes.length; const bedtimeStdDev = Math.sqrt(bedtimeMinutes.map(x => Math.pow(x - avgBedtime, 2)).reduce((a, b) => a + b) / bedtimeMinutes.length); const consistencyScore = Math.max(0, 100 - (bedtimeStdDev * 1.5)); // 30min std dev = 55 score // Quality Score (20%) const avgQuality = logs.reduce((a, b) => a + Number(b.quality), 0) / logs.length; const qualityScore = (avgQuality / 5) * 100; const totalScore = (durationScore * 0.4) + (consistencyScore * 0.4) + (qualityScore * 0.2); return Math.round(totalScore); } function generateAIRecommendations(logs, score) { const recs = []; const avgDuration = logs.map(log => { const bedtime = new Date(`${log.date}T${log.bedtime}`); const waketime = new Date(`${log.date}T${log.waketime}`); if (waketime < bedtime) waketime.setDate(waketime.getDate() + 1); return (waketime - bedtime) / 3600000; }).reduce((a, b) => a + b, 0) / logs.length; if (score > 85) recs.push({type: 'success', text: "Excellent work! Your sleep schedule is consistent and healthy. Keep it up."}); if (Math.abs(avgDuration - settings.targetDuration) > 1) { recs.push({type: 'warning', text: `Your average sleep of ${avgDuration.toFixed(1)} hours is more than an hour off your target. Adjust your bedtime or wake-up time to get closer to your goal.`}); } else { recs.push({type: 'success', text: `Your average sleep duration is very close to your target of ${settings.targetDuration} hours.`}); } const bedtimeMinutes = logs.map(log => { const [h, m] = log.bedtime.split(':').map(Number); return h * 60 + m; }); const avgBedtime = bedtimeMinutes.reduce((a, b) => a + b, 0) / bedtimeMinutes.length; const bedtimeStdDev = Math.sqrt(bedtimeMinutes.map(x => Math.pow(x - avgBedtime, 2)).reduce((a, b) => a + b) / bedtimeMinutes.length); if (bedtimeStdDev > 45) { recs.push({type: 'warning', text: `Your bedtime varies by over 45 minutes on average. Try to go to bed within the same 30-minute window each night, even on weekends.`}); } else { recs.push({type: 'success', text: `Your bedtime consistency is great, which is key for a strong circadian rhythm.`}); } return recs; } function renderConsistencyChart(logs) { const ctx = document.getElementById('consistencyChart'); if (!ctx) return; const labels = logs.map(log => new Date(log.date)).reverse(); const data = { labels: labels, datasets: [ { label: 'Bedtime', data: logs.map(log => new Date(`${log.date}T${log.bedtime}`)).reverse(), borderColor: '#818CF8', tension: 0.1 }, { label: 'Wake-up Time', data: logs.map(log => { let bedtime = new Date(`${log.date}T${log.bedtime}`); let waketime = new Date(`${log.date}T${log.waketime}`); if (waketime < bedtime) waketime.setDate(waketime.getDate() + 1); return waketime; }).reverse(), borderColor: '#FBBF24', tension: 0.1 } ] }; if (consistencyChart) consistencyChart.destroy(); consistencyChart = new Chart(ctx, { type: 'line', data: data, options: { responsive: true, maintainAspectRatio: false, scales: { x: { type: 'time', time: { unit: 'day' }, ticks: { color: '#94A3B8' } }, y: { type: 'time', time: { unit: 'hour', displayFormats: { hour: 'h a' } }, ticks: { color: '#94A3B8' } } }, plugins: { legend: { labels: { color: '#E2E8F0' } } } } }); } // --- PDF GENERATION --- // function generatePDF() { const { jsPDF } = window.jspdf; const pdf = new jsPDF({ unit: 'pt', format: 'a4' }); let y = 40; const margin = 40; const pdfWidth = pdf.internal.pageSize.getWidth(); pdf.setFontSize(20); pdf.setFont('helvetica', 'bold'); pdf.text('Sleep Analysis Report', pdfWidth / 2, y, { align: 'center' }); y += 40; pdf.setFontSize(12); pdf.text(`Overall Sleep Score: ${sleepScoreStat.textContent}`, margin, y); y += 40; pdf.setFontSize(14); pdf.setFont('helvetica', 'bold'); pdf.text('AI Recommendations', margin, y); y += 20; pdf.setFont('helvetica', 'normal'); pdf.setFontSize(10); aiRecommendationsEl.querySelectorAll('p').forEach(p => { const lines = pdf.splitTextToSize(p.textContent, pdfWidth - margin * 2); pdf.text(lines, margin, y); y += lines.length * 12 + 6; }); y += 20; pdf.setFontSize(14); pdf.setFont('helvetica', 'bold'); pdf.text('Recent Sleep Logs', margin, y); const headers = [['Date', 'Bedtime', 'Wake-up', 'Duration (h)', 'Quality (1-5)']]; const body = sleepLogs.slice(0, 14).map(log => { const bedtime = new Date(`${log.date}T${log.bedtime}`); const waketime = new Date(`${log.date}T${log.waketime}`); if (waketime < bedtime) waketime.setDate(waketime.getDate() + 1); const duration = ((waketime - bedtime) / 3600000).toFixed(1); return [log.date, log.bedtime, log.waketime, duration, log.quality]; }); pdf.autoTable({ startY: y + 20, head: headers, body: body, theme: 'grid', headStyles: { fillColor: [79, 70, 229] } }); pdf.save('Sleep-Report.pdf'); } // --- TAB NAVIGATION --- // function setActiveTab(index) { tabs.forEach((tab, i) => tab.classList.toggle('active', i === index)); tabPanels.forEach((panel, i) => panel.classList.toggle('hidden', i !== index)); if (index === 0) updateDashboard(); } // --- KICK IT OFF --- // initializeApp(); });
Scroll to Top