/** * Liquid effect adapted from https://www.framer.com/@kevin-levron/ */ import { useEffect, useRef, useCallback, useState } from "react"; import LiquidBackground from "../utils/liquid1.min.js"; type LiquidApp = ReturnType; interface LiquidIntroProps { onEnter: () => void; } export function LiquidIntro({ onEnter }: LiquidIntroProps) { const canvasRef = useRef(null); const appRef = useRef(null); const [fading, setFading] = useState(false); const [ready, setReady] = useState(false); useEffect(() => { let disposed = false; async function init() { if (disposed || !canvasRef.current) return; const app = LiquidBackground(canvasRef.current); appRef.current = app; app.loadImage("/liquid-dark.webp"); app.liquidPlane.material.metalness = 0.75; app.liquidPlane.material.roughness = 0.58; app.liquidPlane.uniforms.displacementScale.value = 5; app.setRain(false); // Small delay to let the first frame render setTimeout(() => { if (!disposed) setReady(true); }, 300); } init(); return () => { disposed = true; if (appRef.current) { appRef.current.dispose(); appRef.current = null; } }; }, []); const handleClick = useCallback(() => { if (fading) return; setFading(true); setTimeout(() => { if (appRef.current) { appRef.current.dispose(); appRef.current = null; } onEnter(); }, 600); }, [fading, onEnter]); return (

LFM2.5{" "} WebGPU

Click anywhere to start

); }