Section 1: Introduction
Predictability minimizes anxiety. This infographic breaks down a
sensory-safe meal plan
designed to balance textures and ensure nutrition.
AI Sensory Lab Section (NEW)
Input any meal to get an instant sensory-safe adaptation guide.
Section 2: Texture Categories (Doughnut Chart)
Section 3: The Weekly Rotation
Note how textures vary to prevent sensory fatigue.
Separate sauce to keep crunch.
Consistent brand and shape.
Strained to remove all bits.
Fully melted, uniform triangles.
Whipped with mixer for zero lumps.
Section 4: Charts
Section 5: AI Caregiver Observations
// --- Export to PNG --- async function exportToPNG() { const btn = document.querySelector('button[>
try { const element = document.getElementById('infographic-content'); // Ensure charts are rendered before capture const canvas = await html2canvas(element, { scale: 2, useCORS: true, backgroundColor: "#f3f4f6", logging: false });
const link = document.createElement('a'); link.download = '101autism-meal-rotation.png'; link.href = canvas.toDataURL('image/png'); link.click(); } catch (error) { console.error("Export failed", error); } finally { btn.innerHTML = originalText; btn.disabled = false; } }
// --- Gemini API Call Wrapper (with Exponential Backoff) --- async function callGemini(prompt, systemInstruction = "") { let delay = 1000; for (let i = 0; i setTimeout(r, delay)); delay *= 2; } } }
// --- Sensory Lab --- async function getSensoryAdaptation() { const input = document.getElementById('meal-input').value; const btn = document.getElementById('lab-btn'); const res = document.getElementById('lab-result'); if (!input) return;
btn.disabled = true; btn.innerText = "Analyzing..."; res.classList.remove('hidden'); res.classList.add('loading-shimmer'); res.innerText = "Thinking...";
try { const prompt = `Provide 3 short sensory adaptations for this meal: "${input}". Categorize them by: 1. Crunchy (high feedback) 2. Smooth (uniform texture) 3. Deconstructed (keeping parts separate). Keep it brief and encouraging for a caregiver.`;
const response = await callGemini(prompt, "You are an expert sensory therapist for children with autism."); res.classList.remove('loading-shimmer'); res.innerText = response; } catch (e) { res.innerText = "Connection error. Please try again."; } finally { btn.disabled = false; btn.innerText = "Analyze"; } }
// --- AI Progress Report --- async function generateAIReport() { const obs = document.getElementById('observations-input').value; const container = document.getElementById('report-container'); const textDiv = document.getElementById('report-text'); const btn = document.getElementById('report-btn');
if (!obs) return;
btn.disabled = true; textDiv.innerText = "Summarizing progress..."; container.classList.remove('hidden');
try { const prompt = `Analyze these caregiver observations: "${obs}". Provide a professional 2-sentence summary of the win and one piece of encouraging advice regarding food exploration.`; const response = await callGemini(prompt, "You are a clinical psychologist specializing in autism sensory support."); textDiv.innerText = response; } catch (e) { textDiv.innerText = "Error generating report."; } finally { btn.disabled = false; } }
// --- TTS Prep Guide --- async function speakPrepTips() { const textToSpeak = "Say warmly: Welcome to your meal rotation. Monday: Nuggets, keep sauce separate. Tuesday: Pasta, use the same brand. Wednesday: Yogurt, whisk until silky. Thursday: Quesadilla, melt fully. Friday: Potatoes, whip until smooth.";
try { const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-tts:generateContent?key=${apiKey}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contents: [{ parts: [{ text: textToSpeak }] }], generationConfig: { responseModalities: ["AUDIO"], speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: "Kore" } } } } }) }); const result = await response.json(); const pcmData = result.candidates[0].content.parts[0].inlineData.data;
// Convert PCM16 to WAV for playback const playAudio = (base64) => { const binary = atob(base64); const len = binary.length; const bytes = new Uint8Array(len); for (let i = 0; i { const pcm = Uint8Array.from(atob(pcmBase64), c => c.charCodeAt(0)).buffer; const wav = new Uint8Array(44 + pcm.byteLength); const view = new DataView(wav.buffer); const writeString = (offset, string) => { for (let i = 0; i { const label = items[0].chart.data.labels[items[0].dataIndex]; return Array.isArray(label) ? label.join(' ') : label; } } };
// Texture Chart new Chart(document.getElementById('textureChart'), { type: 'doughnut', data: { labels: ['Crunchy', 'Soft', 'Smooth', 'Chewy', 'Uniform'], datasets: [{ data: [20, 30, 20, 20, 10], backgroundColor: ['#F97316', '#EAB308', '#06B6D4', '#8B5CF6', '#84CC16'], borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { tooltip: commonTooltip, legend: { position: 'bottom' } } } });
// Radar Chart new Chart(document.getElementById('sensoryRadar'), { type: 'radar', data: { labels: ['Crunch', 'Softness', 'Smoothness', 'Predictability', 'Effort'], datasets: [ { label: 'Nuggets', data: [95, 20, 10, 90, 30], borderColor: '#F97316', backgroundColor: 'rgba(249,115,22,0.1)' }, { label: 'Yogurt', data: [0, 80, 100, 95, 20], borderColor: '#06B6D4', backgroundColor: 'rgba(6,182,212,0.1)' } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { tooltip: commonTooltip } } });
// Bar Chart new Chart(document.getElementById('safetyChart'), { type: 'bar', data: { labels: ['Nuggets', 'Pasta', 'Yogurt', 'Quesadilla', 'Potatoes'], datasets: [{ label: 'Safety Score', data: [85, 90, 95, 80, 98], backgroundColor: '#84CC16' }] }, options: { indexAxis: 'y', responsive: true, maintainAspectRatio: false, plugins: { tooltip: commonTooltip } } }); };