tag and network/CSP." ); } function MemoryFlowAnimation() { const [playing, setPlaying] = useState(true); const [showLabels, setShowLabels] = useState(true); const [speed, setSpeed] = useState(1); const [recorderState, setRecorderState] = useState("idle"); // idle | recording const [mediaRecorder, setMediaRecorder] = useState(null); const W = 900; const H = 540; const VIDEO_URL = "http://snowballguitarmethod.com/videezy_brain_1080p.mp4"; // HTTPS note for the user const [httpsBlocked, setHttpsBlocked] = useState(false); useEffect(() => { if (location.protocol === "https:" && VIDEO_URL.startsWith("http://")) { setHttpsBlocked(true); } }, []); const pts = useMemo(() => { const entry = { x: 60, y: H * 0.52 }; const STM = { x: 350, y: H * 0.36 }; const WM = { x: 450, y: H * 0.60 }; const LTM = { x: 660, y: H * 0.34 }; return { entry, STM, WM, LTM }; }, [H]); const segments = useMemo( () => [ { id: "seg1", from: pts.entry, to: pts.STM }, { id: "seg2", from: pts.STM, to: pts.WM }, { id: "seg3", from: pts.WM, to: pts.LTM }, ], [pts] ); // Guards if FM failed to load const dotCtl = useAnimation ? useAnimation() : null; const pulseSTM = useAnimation ? useAnimation() : null; const pulseWM = useAnimation ? useAnimation() : null; const pulseLTM = useAnimation ? useAnimation() : null; const segCtl = useAnimation ? [useAnimation(), useAnimation(), useAnimation()] : [null,null,null]; const baseDuration = 6; const d = baseDuration / Math.max(0.25, speed); useEffect(() => { if (!dotCtl || !pulseSTM || !pulseWM || !pulseLTM || !segCtl[0]) return; let isCancelled = false; async function run() { while (!isCancelled) { if (!playing) { await new Promise((r) => setTimeout(r, 100)); continue; } dotCtl.set({ x: pts.entry.x, y: pts.entry.y, opacity: 1, scale: 1 }); await Promise.all([ ...segCtl.map((c) => c.start({ opacity: 0.25 })), pulseSTM.start({ opacity: 0 }), pulseWM.start({ opacity: 0 }), pulseLTM.start({ opacity: 0 }), ]); // Entry -> STM await segCtl[0].start({ opacity: 1, transition: { duration: 0.2 } }); await dotCtl.start({ x: [pts.entry.x, pts.STM.x], y: [pts.entry.y, pts.STM.y], transition: { duration: d * 0.33, ease: "easeInOut" }, }); await pulseSTM.start({ opacity: [0, 1, 0], transition: { duration: d * 0.12 } }); // STM -> WM await segCtl[1].start({ opacity: 1, transition: { duration: 0.2 } }); await dotCtl.start({ x: [pts.STM.x, pts.WM.x], y: [pts.STM.y, pts.WM.y], transition: { duration: d * 0.28, ease: "easeInOut" }, }); await pulseWM.start({ opacity: [0, 1, 0], transition: { duration: d * 0.12 } }); // WM -> LTM await segCtl[2].start({ opacity: 1, transition: { duration: 0.2 } }); await dotCtl.start({ x: [pts.WM.x, pts.LTM.x], y: [pts.WM.y, pts.LTM.y], transition: { duration: d * 0.26, ease: "easeInOut" }, }); await pulseLTM.start({ opacity: [0, 1, 0], transition: { duration: d * 0.14 } }); await dotCtl.start({ scale: [1, 1.2, 1], transition: { duration: d * 0.08 } }); await new Promise((r) => setTimeout(r, 180 / speed)); await dotCtl.start({ opacity: [1, 0], transition: { duration: 0.3 } }); await new Promise((r) => setTimeout(r, 250 / speed)); } } run(); return () => { isCancelled = true; }; }, [playing, d, pts, dotCtl, pulseSTM, pulseWM, pulseLTM, segCtl, speed]); const line = (from, to) => `M ${from.x} ${from.y} L ${to.x} ${to.y}`; // --- Simple tab capture recorder (WebM) --- async function startRecording() { try { const stream = await navigator.mediaDevices.getDisplayMedia({ video: { frameRate: 30 }, audio: false }); const rec = new MediaRecorder(stream, { mimeType: "video/webm;codecs=vp9" }); rec.ondataavailable = (e) => { if (e.data.size > 0) { const url = URL.createObjectURL(new Blob([e.data], { type: "video/webm" })); const a = document.createElement("a"); a.href = url; a.download = "memory-flow-capture.webm"; a.click(); URL.revokeObjectURL(url); } }; rec.onstop = () => { stream.getTracks().forEach(t => t.stop()); setRecorderState("idle"); }; setRecorderState("recording"); rec.start(); // store recorder so we can stop later window.__rec = rec; } catch (err) { console.error("Recording failed:", err); alert("Recording was blocked or canceled. Choose \"This Tab\" when prompted for a clean capture."); } } function stopRecording() { if (window.__rec && recorderState === "recording") { window.__rec.stop(); } } return (
{httpsBlocked && (
The background video is served over http:// while this page is https:// — most browsers will block mixed content. View this page over http:// or host the video on https.
)} {/* Controls */}
{/* Recorder */}
Tip: Choose This Tab when prompted for the cleanest capture.
{/* Stage with video background */}
{/* Entry arrow */} {showLabels && Input} {segments.map((s, i) => ( ))} {showLabels && Short-Term Memory} {showLabels && Working Memory} {showLabels && Long-Term Memory}
); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render();