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 = () => ( Supermemory - #1 Product of the Day on Product Hunt ); 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 Medium Notion Reddit Twitter

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.

); }