Ich habe ein Webflow-Showcase erstellt, um diese Implementierung in Aktion zu zeigen. Du kannst dir sowohl die einfache als auch die erweiterte Version anschauen – und wenn du es selbst ausprobieren möchtest, kannst du das Projekt ganz einfach klonen und duplizieren.
Spline hat kürzlich eine leistungsstarke Funktion eingeführt: den Export deiner 3D-Szenen als selbst gehostete Pakete. Damit hast du volle Kontrolle darüber, wie und wo deine Spline-Szenen gehostet werden – ideal für individuelle Webseitenprojekte mit besserer Performance, CDN-Geschwindigkeit und Unabhängigkeit von Drittanbietern.
In diesem Tutorial zeige ich dir Schritt für Schritt, wie du eine Spline-Szene als selbst gehostetes Paket exportierst, die Dateien bei BunnyCDN hochlädst und die Szene mithilfe eines <canvas>-Elements und der Spline-Runtime in Webflow einbettest.
Auch wenn dieses Tutorial BunnyCDN als Hosting-Plattform verwendet, kannst du jeden beliebigen statischen Filehost nutzen, der CORS-Header und korrekte MIME-Typen unterstützt. Alternativen sind zum Beispiel Cloudflare R2, AWS S3, Netlify, Vercel oder dein eigener Server. BunnyCDN wird hier vorgestellt, da es besonders schnell, günstig und einfach zu bedienen ist.
Du solltest folgende Dateien sehen:
https://dein-cdn.b-cdn.net/spline-szene/
<canvas id="spline-container" style="width:100%; height:600px;"></canvas>
<script type="module">
import { Application } from 'https://dein-cdn.b-cdn.net/spline-szene/runtime.js';
const canvas = document.getElementById('spline-container');
const app = new Application(canvas, {
wasmPath: 'https://dein-cdn.b-cdn.net/spline-szene/'
});
app.load('https://dein-cdn.b-cdn.net/spline-szene/scene.splinecode')
.then(() => console.log('Szene geladen'))
.catch((err) => console.error('Ladefehler', err));
</script>Wenn alles korrekt eingerichtet ist, sollte deine Spline-Szene jetzt direkt und performant in deiner Webflow-Seite angezeigt werden. Du profitierst von mehr Flexibilität, schnellen Ladezeiten und voller Kontrolle über deine Assets.
Falls etwas nicht funktioniert, prüfe die Konsole deines Browsers auf Fehlermeldungen und stelle sicher, dass alle URLs korrekt und öffentlich erreichbar sind.
Halte Ausschau nach zukünftigen Updates von Spline – neue Exportformate und Runtime-Verbesserungen machen das Self-Hosting noch einfacher.
Wenn deine Webflow-Seite mehrere Spline-Szenen enthält, brauchst du ein Setup, das Performance-Probleme vermeidet und wiederholten Code reduziert. Die folgende optimierte Lösung sorgt dafür, dass alle Szenen effizient geladen werden – ohne dass das Runtime-Modul mehrfach geladen wird – und bleibt dabei sauber und skalierbar.
<script>
document.addEventListener("DOMContentLoaded", async function () {
// Pfad zu deinen selbst gehosteten Runtime- und .splinecode-Dateien
const WASM_PATH = 'https://YOUR_CDN_URL_HERE/spline-files/';
// Lade das Runtime-Modul einmal und verwende es wieder
let runtimeModulePromise = import(WASM_PATH + 'runtime.js');
const getRuntime = () => runtimeModulePromise;
// Einfache Warteschlange, damit Szenen nacheinander initialisiert werden
let queue = Promise.resolve();
const enqueue = (task) => (queue = queue.then(task).catch(() => {}));
// Verfolgung, welche Szenen bereits geladen wurden
const initialized = new Set();
// Prüft, ob ein Canvas versteckt ist
const isHidden = (el) => {
if (!el) return true;
const cs = window.getComputedStyle(el);
return cs.display === 'none' || cs.visibility === 'hidden';
};
// Lädt eine Szene in ein Canvas
async function loadScene({ canvasId, splineUrl }) {
if (initialized.has(canvasId)) return;
const canvas = document.getElementById(canvasId);
if (!canvas || isHidden(canvas)) return;
await enqueue(async () => {
const { Application } = await getRuntime();
const app = new Application(canvas, { wasmPath: WASM_PATH });
await app.load(splineUrl);
initialized.add(canvasId);
console.log(`✅ Spline-Szene geladen: ${canvasId}`);
});
}
// Lazy Loading per IntersectionObserver
function lazyLoadScene(cfg) {
const canvas = document.getElementById(cfg.canvasId);
if (!canvas || isHidden(canvas) || initialized.has(cfg.canvasId)) return;
const io = new IntersectionObserver(async (entries, obs) => {
if (!entries[0].isIntersecting) return;
obs.disconnect();
await loadScene(cfg);
}, {
rootMargin: '1000px 0px', // Frühzeitig laden für bessere UX
threshold: 0.05
});
io.observe(canvas);
}
// ---------- KONFIGURATION START ----------
// Hier definierst du alle Szenen
const scenes = [
{
canvasId: 'spline-scene-1',
splineUrl: 'https://YOUR_CDN_URL_HERE/spline-files/scene-1/scene.splinecode'
},
{
canvasId: 'spline-scene-2',
splineUrl: 'https://YOUR_CDN_URL_HERE/spline-files/scene-2/scene.splinecode'
},
{
canvasId: 'spline-scene-3',
splineUrl: 'https://YOUR_CDN_URL_HERE/spline-files/scene-3/scene.splinecode'
}
// Weitere Szenen können hier hinzugefügt werden...
];
// ---------- KONFIGURATION ENDE ----------
// Prefetch: Lade alle .splinecode-Dateien im Hintergrund vor
const idle = window.requestIdleCallback || ((fn) => setTimeout(fn, 250));
idle(() => {
scenes.forEach(s => {
try {
fetch(s.splineUrl, { mode: 'no-cors' });
} catch (_) {}
});
});
// Beobachter für alle Szenen aktivieren
scenes.forEach(lazyLoadScene);
});
</script>
Füge an jeder Stelle auf deiner Seite, an der eine Spline-Szene erscheinen soll, folgendes ein (z. B. als Embed-Element):
<canvas id="spline-scene-1" style="width: 100%; height: 100%;"></canvas>
<canvas id="spline-scene-2" style="width: 100%; height: 100%;"></canvas>
<canvas id="spline-scene-3" style="width: 100%; height: 100%;"></canvas>
Diese Lösung ist ideal, wenn du mehrere Spline-Szenen auf einer Seite nutzt und dabei Wert auf:
legst.
Für einzelne Szenen reicht auch das ursprüngliche Setup – aber sobald es mehrere werden, ist dieses Setup deutlich überlegen.