Wie man die 3D-Web-Performance optimiert: Three.js, R3F und der lazy geladene Hero
Ein 3D-Hero sieht toll in einem Produkt-Launch-Video aus und toll in Ihrem Lighthouse-Audit-Durchfall. Hier ist, wie wir eine Three.js-Homepage ausgeliefert haben, die das initiale Bundle schlank und die interaktive Szene warm hält.
Die 3D-Homepage von 2run.be baut auf Three.js mit React Three Fiber. Die erste Version dieser Seite fügte dem initialen Bundle 600 KB hinzu und drückte die Lighthouse-Mobile-Performance unter 60. Die aktuelle Version liefert die 3D-Szene erst aus, nachdem die Nutzerin nah herangescrollt ist, und wiegt auf dem kritischen Pfad ein Zehntel der ersten Version. Hier ist, wie wir dorthin kamen.
Was in einer 3D-Web-Szene steckt
Ein typischer R3F-Szenengraph zieht herein:
- Three.js selbst (~150 KB gepackter Kern, mehr mit Controls, Loadern, Postprocessing).
- React Three Fiber (~30 KB) und Drei (~50 KB+ je nach genutzten Helpers).
- Die Geometrie- und Material-Loader, die Sie tatsächlich nutzen.
- Die Shader-Programme, falls Sie eigene schreiben.
Eine konservative Baseline sind 250–350 KB gepackt für eine nicht-triviale Szene. Eine schwerere Szene mit Physik, Postprocessing und eigenen Shadern kann 600 KB sprengen. Diese Zahl ist die gesamte Performance-Story: sie konkurriert mit dem Rest der Seite um das Budget auf dem kritischen Pfad.
Zwei Ansätze
Ansatz A: auf dem kritischen Pfad ausliefern
Sie verpflichten sich, dass die 3D-Szene Teil des ersten Paint ist. Die gesamte Szene, inklusive Geometrie-Loadern und Controls, landet im initialen Bundle. Die Nutzerin sieht die 3D-Szene, sobald das HTML ankommt. Die First-Paint-Story ist die 3D-Story.
Das funktioniert, wenn:
- Der Rest der Seite wirklich minimal ist (ein Portfolio-Hero, eine Landingpage).
- Die 3D-Szene das Wertversprechen ist – die Szene verkaufen heißt das Produkt verkaufen.
- Mobile Nutzerinnen in Ihrem Zielmarkt meistens im WLAN sind.
Es funktioniert nicht, wenn:
- Die 3D-Szene dekorativ ist, nicht funktional.
- Die Seite mehr Content hat (Text, CTAs, ein Formular) als nur die Szene.
- Mobile-LCP Teil Ihres Core-Web-Vitals-Versprechens ist.
Ansatz B: nach erster Interaktion lazy laden
Sie verschieben die 3D-Szene auf einen next/dynamic-Import mit ssr: false und triggern den Load erst, wenn die Nutzerin nah an den Hero scrollt oder nach einer kleinen Verzögerung. Das initiale Bundle trägt nie die 3D-Payload. Die Szene erscheint, wenn die Nutzerin dabei ist, sie zu sehen.
Das funktioniert, wenn:
- Der Hero-Content above the fold auf eigenen Beinen stehen kann (ein Bild, ein Gradient, eine typografische Aussage).
- Die First-Paint-Story das Markenversprechen ist, nicht die 3D-Szene.
- Sie einen
IntersectionObserveroder einen Scroll-Listener haben, um den Load zu triggern.
Das ist der Ansatz, den wir für die 2Run-Homepage gewählt haben. Der Trade-off:
- Pro: Das initiale Bundle fällt um 600 KB. Die Lighthouse-Mobile-Performance springt von unter 60 auf über 90.
- Contra: Die Nutzerin sieht ~0,5 s lang einen Placeholder, bevor die Szene füllt. Bei guter Platzierung der Trigger-Zone wirkt das, als würde die Szene „emporsteigen“ statt „einzublenden“.
Die Implementierungsskizze
Das Muster, auf die Essenz reduziert:
- Ein
useInView-Hook, der einen Boolean zurückgibt, wenn das Target-Ref im Viewport ist. - Ein
next/dynamic-Import für die schwere Szenen-Komponente mitssr: falseund einem leichtgewichtigen Loading-Skeleton. - Der Hero-Bereich rendert Typografie und CTA eagerly und mountet die Szene dann bedingt, wenn
inViewauf true kippt.
Ein subtiles Gotcha: die Szenen-Komponente sollte auch einen Reduced-Motion-Fallback akzeptieren. Nutzerinnen, die im Betriebssystem reduzierte Bewegung angefordert haben, bekommen den Typografie-Hero ohne die 3D-Szene – und Lighthouse-Performance ist nicht die einzige Metrik, die zählt.
Mobilie und die untere Preisklasse
Die ehrliche Wahrheit über mobile 3D: ein Mittelklasse-Android bei 30 °C Akku-Drosselung kann noch immer nicht mit einem Desktop-Browser bei gleicher Szenenkomplexität mithalten. Zwei Wege:
- Erkennen und degradieren. Nutzen Sie einen
gl-Capability-Check (renderer.getContext() und ein kleines Benchmark) und dienen Sie eine niedrigpolygones Szene auf schwächeren Geräten aus. Beste UX, kostet Engineering-Zeit. - Bedingungslos rendern und die Kosten akzeptieren. In Ordnung für B2B-Käuferinnen auf firmenverwalteten Geräten. Nicht in Ordnung für Konsumenten-Publika.
Wir wählten den ersten Weg. Die Erkennungslogik läuft einmal pro Session, das Ergebnis wird in sessionStorage gecacht. Die Fallback-Szene nutzt niedrigpolygone Geometrie und lässt den Postprocessing-Pass weg.
Was in dieser Story nicht steht
- Server-Side-Rendering von 3D-Szenen. Es gibt kein SSR für Three.js – das Canvas braucht einen DOM-Kontext. SSR-Stubs für eine 3D-Szene sind meist dekorativ und fügen SSR-Komplexität für begrenzten Gewinn hinzu.
- GPU-Shader-Vorkompilierung. Echte Gewinne, aber außer wenn Sie eine eigene Postprocessing-Schicht ausliefern, reicht der Browser-JIT aus.
- 3D als LCP selbst. 3D als LCP-Element zu targetieren ist der Weg zu einem schnellen Initial-Paint für ein 3D-Portfolio und einem langsamen LCP für eine contentlastige Seite. Wählen Sie das, was Sie über Ihre Seite für wahr halten wollen.
Das Fazit
Behandeln Sie 3D-Web-Szenen wie jedes andere schwere Feature: profilieren Sie das Bundle, verschieben Sie, was Sie können, rendern Sie die richtige Stufe auf dem richtigen Gerät und verantworten Sie den Trade-off explizit. Eine 3D-Szene, die die Core Web Vitals verfehlt, ist kein kreativer Gewinn – sie ist eine Launch-Site, die nicht rankt.
