import { Suspense, useEffect, useRef, useState } from 'react';
import Spline from '@splinetool/react-spline';
interface SplineBackgroundProps {
scene?: string;
className?: string;
onLoad?: () => void;
}
// Fallback component for loading state
function SplineFallback({ message = 'Loading 3D Scene...' }: { message?: string }) {
return (
);
}
export default function SplineBackground({
scene = "https://prod.spline.design/6Wq8T6XmHxrGMwni/scene.splinecode",
className = "",
onLoad
}: SplineBackgroundProps) {
const [error, setError] = useState(null);
const [ready, setReady] = useState(false);
const containerRef = useRef(null);
const isSplineCode = scene.trim().toLowerCase().endsWith('.splinecode');
// Preflight check: ensure the scene URL is reachable before trying to load Spline
useEffect(() => {
let cancelled = false;
setError(null);
setReady(false);
(async () => {
try {
// Use no-cors GET: many CDNs block HEAD and CORS; opaque means we cannot inspect but still OK to try
const res = await fetch(scene, { method: 'GET', mode: 'no-cors' });
if (!cancelled) setReady(true);
} catch (e) {
console.error('Spline scene pre-check failed', e);
if (!cancelled) setError('Failed to reach 3D scene');
}
})();
return () => { cancelled = true; };
}, [scene]);
// Handle WebGL context loss gracefully
useEffect(() => {
const el = containerRef.current;
if (!el) return;
const onContextLost = (ev: Event) => {
console.warn('WebGL context lost');
setError('Graphics context lost');
ev.preventDefault();
};
const onContextRestored = () => {
console.info('WebGL context restored');
setError(null);
};
el.addEventListener('webglcontextlost', onContextLost as EventListener, false);
el.addEventListener('webglcontextrestored', onContextRestored as EventListener, false);
return () => {
el.removeEventListener('webglcontextlost', onContextLost as EventListener, false);
el.removeEventListener('webglcontextrestored', onContextRestored as EventListener, false);
};
}, []);
if (error) {
return (
);
}
// If the provided URL is not a .splinecode, use an iframe fallback. This supports community/share links.
if (!isSplineCode) {
return (
{ready ? (
) : (
)}
);
}
return (
}>
{ready ? (
{
console.error('Spline load error', e);
setError('Failed to load 3D scene');
}}
style={{ width: '100%', height: '100%', background: 'transparent' }}
/>
) : (
)}
);
}
// Alternative Spline scenes for different purposes
export const SplineScenes = {
// Abstract floating elements
abstract: "https://prod.spline.design/6Wq8T6XmHxrGMwni/scene.splinecode",
// Microphone visualization
microphone: "https://prod.spline.design/kR3K8EqaAlKZXBPv/scene.splinecode",
// Speaker/audio visualization
speaker: "https://prod.spline.design/m8rFoLJGtKVQZuE5/scene.splinecode",
// Particle system
particles: "https://prod.spline.design/K7n4XGsOzMqBgEF1/scene.splinecode"
};