import { useEffect, useRef } from "react"; import { Logo } from "./icons/Logo"; import { motion, useMotionTemplate, useScroll, useSpring, useTransform } from "framer-motion"; // Interfaces interface Memory { x: number; y: number; size: number; type: "bookmark" | "note" | "tweet" | "doc"; color: string; orbitRadius: number; orbitSpeed: number; orbitOffset: number; opacity: number; } // Components const ProductHuntBadge = () => ( ); const SupermemoryBackground = () => { const canvasRef = useRef(null); const memoriesRef = useRef([]); const rafRef = useRef(); const centerRef = useRef({ x: 0, y: 0 }); const circleRadiusRef = useRef(150); const targetRadiusRef = useRef(0); useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; const resize = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; centerRef.current = { x: canvas.width / 2, y: canvas.height / 2, }; targetRadiusRef.current = Math.sqrt(Math.pow(canvas.width, 2) + Math.pow(canvas.height, 2)) / 1.5; }; const createMemories = () => { const memories: Memory[] = []; const types: ("bookmark" | "note" | "tweet" | "doc")[] = ["bookmark", "note", "tweet", "doc"]; const colors = { bookmark: "#3B82F6", note: "#10B981", tweet: "#60A5FA", doc: "#818CF8", }; const orbits = [250, 350, 450]; orbits.forEach((orbitRadius, orbitIndex) => { const memoriesInOrbit = 8 + orbitIndex * 4; for (let i = 0; i < memoriesInOrbit; i++) { const type = types[i % 4]; const angle = (Math.PI * 2 * i) / memoriesInOrbit; memories.push({ x: centerRef.current.x + Math.cos(angle) * orbitRadius, y: centerRef.current.y + Math.sin(angle) * orbitRadius, size: 3 + Math.random() * 2, type, color: colors[type], orbitRadius, orbitSpeed: (0.1 + Math.random() * 0.05) * (1 - orbitIndex * 0.2), orbitOffset: angle, opacity: 0.15 + Math.random() * 0.15, }); } }); return memories; }; const drawMemory = (ctx: CanvasRenderingContext2D, memory: Memory, time: number) => { const angle = memory.orbitOffset + time * memory.orbitSpeed; memory.x = centerRef.current.x + Math.cos(angle) * memory.orbitRadius; memory.y = centerRef.current.y + Math.sin(angle) * memory.orbitRadius; const dx = memory.x - centerRef.current.x; const dy = memory.y - centerRef.current.y; const distanceFromCenter = Math.sqrt(dx * dx + dy * dy); if (distanceFromCenter <= circleRadiusRef.current) { // Draw connections memoriesRef.current.forEach((otherMemory) => { if (memory === otherMemory) return; const connectionDx = memory.x - otherMemory.x; const connectionDy = memory.y - otherMemory.y; const connectionDistance = Math.sqrt( connectionDx * connectionDx + connectionDy * connectionDy, ); const otherDx = otherMemory.x - centerRef.current.x; const otherDy = otherMemory.y - centerRef.current.y; const otherDistanceFromCenter = Math.sqrt(otherDx * otherDx + otherDy * otherDy); if (connectionDistance < 80 && otherDistanceFromCenter <= circleRadiusRef.current) { const opacity = (1 - connectionDistance / 80) * 0.04; ctx.beginPath(); ctx.moveTo(memory.x, memory.y); ctx.lineTo(otherMemory.x, otherMemory.y); ctx.strokeStyle = `rgba(59, 130, 246, ${opacity})`; ctx.lineWidth = 0.5; ctx.stroke(); } }); // Draw node const gradient = ctx.createRadialGradient( memory.x, memory.y, 0, memory.x, memory.y, memory.size * 2, ); gradient.addColorStop(0, memory.color.replace(")", `,${memory.opacity})`)); gradient.addColorStop(1, memory.color.replace(")", ",0)")); ctx.beginPath(); ctx.arc(memory.x, memory.y, memory.size, 0, Math.PI * 2); ctx.fillStyle = gradient; ctx.fill(); } }; memoriesRef.current = createMemories(); const animate = () => { if (!ctx || !canvas) return; ctx.fillStyle = "rgba(17, 24, 39, 1)"; ctx.fillRect(0, 0, canvas.width, canvas.height); const time = Date.now() * 0.001; // Grow circle with easing const radiusDiff = targetRadiusRef.current - circleRadiusRef.current; if (Math.abs(radiusDiff) > 1) { circleRadiusRef.current += radiusDiff * 0.02; } // Create clipping region ctx.save(); ctx.beginPath(); ctx.arc(centerRef.current.x, centerRef.current.y, circleRadiusRef.current, 0, Math.PI * 2); ctx.clip(); // Draw orbit paths [250, 350, 450].forEach((radius) => { ctx.beginPath(); ctx.arc(centerRef.current.x, centerRef.current.y, radius, 0, Math.PI * 2); ctx.strokeStyle = "rgba(255, 255, 255, 0.02)"; ctx.stroke(); }); memoriesRef.current.forEach((memory) => drawMemory(ctx, memory, time)); ctx.restore(); rafRef.current = requestAnimationFrame(animate); }; resize(); window.addEventListener("resize", resize); animate(); return () => { if (rafRef.current) cancelAnimationFrame(rafRef.current); window.removeEventListener("resize", resize); }; }, []); return ( ); }; export default function Landing() { const { scrollYProgress } = useScroll(); const scrollProgress = useSpring(scrollYProgress); const boxOpacity = useTransform(scrollYProgress, [0.3, 0.6], [0, 1]); const boxScale = useTransform(scrollYProgress, [0.3, 0.6], [0.8, 1]); return ( supermemory.ai Follow us Get Started Your second brain for all your saved content Save anything from anywhere. Supermemory connects your bookmarks, notes, and research into a powerful, searchable knowledge base. {/* Chrome extension for one-click saving AI-powered search across all your content Integrates with Notion, Twitter, and more */} Try it free Star on GitHub All your knowledge in one place Supermemory intelligently organizes and connects your saved content, making it easy to find and use when you need it. ); }
Supermemory intelligently organizes and connects your saved content, making it easy to find and use when you need it.