{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "webgl-ladeszene-fortschritt",
 "name": "WebGL-Ladeszene mit echtem Fortschritt",
 "name_en": "WebGL loading screen with real progress",
 "kurz": "Ein Konfigurator, der ehrlich lädt: Prozent nach Bytes, Dateiliste mit Häkchen, dann taucht die Leuchte aus dem Dunkel auf.",
 "kategorie": "Preloader & Intro",
 "kategorie_slug": "preloader-intro",
 "kategorie_nr": 2,
 "bau_chat": "C3",
 "prioritaet": "B",
 "trendstatus": "etabliert",
 "belegstatus": "belegt",
 "website_belege": 2,
 "stimmung": [
  "präzise",
  "filmisch"
 ],
 "stimmung_roh": [
  "immersiv",
  "technisch",
  "filmisch"
 ],
 "technik": [
  "three",
  "webgl-shader",
  "gsap",
  "canvas"
 ],
 "technik_gruppen": [
  "gsap",
  "webgl",
  "canvas"
 ],
 "leistungskosten": "hoch",
 "steckbrief": {
  "beschreibung": "1) Ein Vollbild-Quad mit Shader (oder ein DOM-Overlay) verdeckt die 3D-Szene. 2) THREE.LoadingManager meldet, welche Dateien fertig sind (Modelle, Texturen, Umgebungsbild). Ein Balken (scaleX) und ein Zähler zeigen den Fortschritt. 3) Ist alles geladen, wird die Szene kompiliert und enthüllt: Alpha-Uniform von 1 auf 0, Noise-Dissolve oder Pixelraster. 4) Gleichzeitig fliegt die Kamera aus der Dunkelheit auf ihre Startposition.",
  "warum_es_wirkt": "Die Wartezeit ist ehrlich: Man sieht, dass etwas passiert und wie lange es noch dauert. Die Enthüllung macht aus dem Warten einen Auftakt. Die Szene erscheint fertig und ruckelfrei, statt Stück für Stück aufzubauen.",
  "wann_einsetzen": "Bei 3D-lastigen Seiten, deren Szene vor der ersten Interaktion vollständig geladen sein muss: Produktkonfiguratoren, immersive Portfolios, Kampagnen. Im Shop nur für die 3D-Bühne, nie für die ganze Seite.",
  "wann_nicht": "Wenn die Szene progressiv laden kann (erst Platzhalter oder Low-Poly, dann Details). Das ist dem Ladebildschirm vorzuziehen. Nicht vor normalen Inhalten wie Text, Produktbildern oder Kaufbox.",
  "varianten": [
   "Shader-Overlay mit uAlpha-Fade, 3000 ms (Three.js Journey)",
   "Noise-Dissolve des Overlays mit glühender Kante",
   "Pixelraster: Zellen bis 42 px verschwinden diagonal, expoInOut (Lusion)",
   "Progressives Laden ohne Ladebildschirm (Platzhaltergeometrie)",
   "Kleinere Szene statt Ladebildschirm: ZERO hat laut Codrops-Artikel über 1 GB 3D-Assets auf unter 10 MB komprimiert"
  ],
  "umsetzung": "const manager = new THREE.LoadingManager(onLoad, (url, geladen, gesamt) => { … }); new GLTFLoader(manager), new TextureLoader(manager) usw. In onLoad: renderer.compile(scene, camera), dann gsap.to(overlay.uniforms.uAlpha, { value: 0, duration: 3 }) wie bei Three.js Journey. Ohne GSAP genügt ein eigener Tween in der Bildschleife (die Demo nutzt cubic-bezier(0.25, 0.1, 0.25, 1)).\n\nFallstrick: onProgress zählt Dateien, nicht Bytes. Bei einer großen GLB steht der Balken lange still und springt dann. Besser: Dateigrößen aus einem Build-Manifest nehmen und den Byte-Fortschritt pro Datei (XHR-progress) addieren. Die Anzeige per Lerp glätten und 100 % kurz stehen lassen, bevor enthüllt wird. Draco oder Meshopt (Geometrie) und KTX2 (Texturen) verkürzen die Ladezeit. Shader-Kompilierung nach dem Laden kann ruckeln, deshalb renderer.compile() vor der Enthüllung. Die Overlay-Farbe im Shader mit #include <colorspace_fragment> ausgeben, sonst wirkt sie zu dunkel.",
  "mobil": "Leichtere Assets ausliefern (Geräteklasse erkennen, kleinere Texturen). Auf sehr schwachen Geräten oder ohne WebGL ein statisches Bild oder Video zeigen. Pixelratio begrenzen (höchstens 2).",
  "reduzierte_bewegung": "Kein Kameraflug. Die Szene per kurzem Fade (bis 300 ms) oder sofort zeigen.",
  "barrierefreiheit": "Balken mit role=progressbar und aria-valuenow. Textliche Beschreibung der Szeneninhalte. Ein Knopf „Ohne 3D ansehen“ bricht das Laden ab und zeigt ein Bild. Die übrige Seite (Kaufbox, Text) ist während des Ladens voll bedienbar."
 },
 "messwerte": {
  "easing": "ease.expoInOut (Lusion)",
  "dauer_ms": "3000 Overlay-Fade",
  "stagger_ms": "",
  "distanz": "",
  "sonstiges": "Lusion: Pixelraster max. 42 px; Immersive Garden: Fortschrittslinie 1px, opacity .2",
  "herkunft": "beobachtet",
  "quelle": "https://lusion.co/_astro/hoisted.CUO_IjfL.js"
 },
 "kurve": {
  "bezier": [
   0.25,
   0.1,
   0.25,
   1
  ],
  "css": "cubic-bezier(0.25, 0.1, 0.25, 1)",
  "name": "ease",
  "dauer_ms": 3000
 },
 "gesehen_bei": [
  {
   "seite": "Three.js Journey, Lektion 'Intro and loading progress'",
   "url": "https://www.threejs-journey.com/lessons/intro-and-loading-progress",
   "auszeichnung": "",
   "beobachtung": "PlaneGeometry(2,2) mit ShaderMaterial, gl_Position ohne Matrizen, uAlpha startet bei 1. Ein LoadingManager hängt an GLTF- und CubeTextureLoader. onLoad: gsap.to(uAlpha,{duration:3, value:0}).",
   "art": "artikel"
  },
  {
   "seite": "Lusion",
   "url": "https://lusion.co/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (hoisted.CUO_IjfL.js, Three.js im Bundle): Preloader-Ziffern plus Canvas #transition-overlay (class TransitionOverlay, loadBarRatio, Pixelraster pixelWidth = min(42, Viewportbreite/30), ease.expoInOut).",
   "art": "website",
   "browser_gemessen": true
  },
  {
   "seite": "Immersive Garden",
   "url": "https://immersive-g.com/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft: HTML .introLoader mit __progressBar (1px-Linie in currentColor, opacity .2, transform-origin 0 0) und __baseline-Wörtern \"Innovative digital experiences studio\" (PS Times 1.944vw); Three.js im Bundle entry.DyxL_KXi.js.",
   "art": "website",
   "browser_gemessen": true
  }
 ],
 "tutorials": [
  {
   "titel": "Intro and loading progress (Three.js Journey)",
   "url": "https://www.threejs-journey.com/lessons/intro-and-loading-progress"
  },
  {
   "titel": "ZERO: The Engineering Behind a Defiant Interactive Narrative (Codrops, 07/2026)",
   "url": "https://tympanus.net/codrops/2026/07/17/zero-the-engineering-behind-a-defiant-interactive-narrative/"
  }
 ],
 "kombinierbar_mit": [
  "loader-wird-layout",
  "seitenwechsel-kamerafahrt",
  "prozent-zaehler"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/preloader-intro/webgl-ladeszene-fortschritt/",
  "meta": "https://cecodesign.de/lab/fundus/preloader-intro/webgl-ladeszene-fortschritt/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/preloader-intro/webgl-ladeszene-fortschritt/demo"
 },
 "herkunft": {
  "quelldateien": [
   "uebergaenge.json",
   "patch-belege-3.json"
  ],
  "pruefnotizen": [
   "patch-belege-3.json: Zwei Produktionsbelege; Awwwards-Inspiration entfernt."
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Ein Konfigurator, der ehrlich lädt: Prozent nach Bytes, Dateiliste mit Häkchen, dann taucht die Leuchte aus dem Dunkel auf.",
  "demo": true,
  "abhaengigkeiten": [
   "three@0.186.0"
  ],
  "werte": {
   "easing": "Fade und Noise: ease = cubic-bezier(0.25, 0.1, 0.25, 1) (Three.js Journey) · Pixelraster: expoInOut (Lusion); der Kameraflug nutzt jeweils dieselbe Kurve",
   "dauer_ms": "Overlay 3000 · Kameraflug 3000 · 100 % stehen 350 vor der Enthüllung · Ladeanzeige ausblenden 300",
   "overlay": "Vollbild-Quad PlaneGeometry(2, 2), ShaderMaterial, gl_Position ohne Matrizen, Farbe #1f1c1a; Fade: uAlpha 1 → 0; Noise und Raster: uProgress 0 → 1",
   "noise": "fbm 4 Oktaven × 2.6 (in Bildhöhen) × 0.7 + 30 % Verlauf von unten; Schwelle mix(0.115, 0.915, uProgress); Glutkante 0.035 in #e8824a",
   "raster": "Zelle min(42, Breite / 30) px × Pixelratio; Reihenfolge 55 % Diagonale (oben links zuerst), 45 % Zufall; Zelle schrumpft über 20 % des Fortschritts",
   "kamera": "Abstand 11 → 6, Höhe −0.5 → −0.12 rad, Winkel −0.9 → 0 rad, Belichtung (toneMappingExposure) 0.12 → 1, NeutralToneMapping, fov 30",
   "fortschritt": "LoadingManager zählt Dateien (itemStart/itemEnd), Balken und Zahl folgen den Bytes aus dem Manifest; Glättung anzeige += (soll − anzeige) × (1 − e^(−8·dt))",
   "balken": "Linie 1 px in currentColor, Spur opacity .2, transform-origin 0 0, scaleX(Anteil) (Immersive Garden)",
   "netz": "simuliert: 1,9 MB/s geteilt auf alle laufenden Dateien, Latenz 90 ms + 15 % je Datei, 8 Dateien, 3,9 MB, größte Datei 1,84 MB",
   "konfigurator": "Glasurwechsel sofort, Swatch 150 ms cubic-bezier(0, 0, 0.2, 1); Größe S/M skaliert 0.8 ↔ 1 in 250 ms cubic-bezier(0, 0, 0.2, 1); Toast öffnen 250 ms (0, 0, 0.2, 1), schließen 200 ms (0.4, 0, 1, 1)",
   "orbit": "Ziehen: 2.4 rad je Bühnenbreite waagerecht, Höhe −0.3 … 0.32 rad, Trägheit 1 − e^(−6·dt); Pendeln ±0.012 rad"
  },
  "ausloeser": "laden, klick, ziehen",
  "touch": "Waagerechtes Ziehen dreht die Ansicht, senkrecht scrollt die Seite weiter (touch-action: pan-y). Dateiliste ausgeblendet, Zahl, Balken und Dateizähler bleiben. Größere Glasur-Swatches (44 px).",
  "reduziert": "Kein Kameraflug, kein Pendeln, keine Eigendrehung: Die Kamera steht auf der Endposition, das Overlay blendet in 300 ms linear aus. Der Fortschritt wird ungeglättet angezeigt.",
  "korrekturen": {
   "beschreibung": "1) Ein Vollbild-Quad mit Shader (oder ein DOM-Overlay) verdeckt die 3D-Szene. 2) THREE.LoadingManager meldet, welche Dateien fertig sind (Modelle, Texturen, Umgebungsbild). Ein Balken (scaleX) und ein Zähler zeigen den Fortschritt. 3) Ist alles geladen, wird die Szene kompiliert und enthüllt: Alpha-Uniform von 1 auf 0, Noise-Dissolve oder Pixelraster. 4) Gleichzeitig fliegt die Kamera aus der Dunkelheit auf ihre Startposition.",
   "warum_es_wirkt": "Die Wartezeit ist ehrlich: Man sieht, dass etwas passiert und wie lange es noch dauert. Die Enthüllung macht aus dem Warten einen Auftakt. Die Szene erscheint fertig und ruckelfrei, statt Stück für Stück aufzubauen.",
   "wann_einsetzen": "Bei 3D-lastigen Seiten, deren Szene vor der ersten Interaktion vollständig geladen sein muss: Produktkonfiguratoren, immersive Portfolios, Kampagnen. Im Shop nur für die 3D-Bühne, nie für die ganze Seite.",
   "wann_nicht": "Wenn die Szene progressiv laden kann (erst Platzhalter oder Low-Poly, dann Details). Das ist dem Ladebildschirm vorzuziehen. Nicht vor normalen Inhalten wie Text, Produktbildern oder Kaufbox.",
   "varianten": [
    "Shader-Overlay mit uAlpha-Fade, 3000 ms (Three.js Journey)",
    "Noise-Dissolve des Overlays mit glühender Kante",
    "Pixelraster: Zellen bis 42 px verschwinden diagonal, expoInOut (Lusion)",
    "Progressives Laden ohne Ladebildschirm (Platzhaltergeometrie)",
    "Kleinere Szene statt Ladebildschirm: ZERO hat laut Codrops-Artikel über 1 GB 3D-Assets auf unter 10 MB komprimiert"
   ],
   "umsetzung": "const manager = new THREE.LoadingManager(onLoad, (url, geladen, gesamt) => { … }); new GLTFLoader(manager), new TextureLoader(manager) usw. In onLoad: renderer.compile(scene, camera), dann gsap.to(overlay.uniforms.uAlpha, { value: 0, duration: 3 }) wie bei Three.js Journey. Ohne GSAP genügt ein eigener Tween in der Bildschleife (die Demo nutzt cubic-bezier(0.25, 0.1, 0.25, 1)).\n\nFallstrick: onProgress zählt Dateien, nicht Bytes. Bei einer großen GLB steht der Balken lange still und springt dann. Besser: Dateigrößen aus einem Build-Manifest nehmen und den Byte-Fortschritt pro Datei (XHR-progress) addieren. Die Anzeige per Lerp glätten und 100 % kurz stehen lassen, bevor enthüllt wird. Draco oder Meshopt (Geometrie) und KTX2 (Texturen) verkürzen die Ladezeit. Shader-Kompilierung nach dem Laden kann ruckeln, deshalb renderer.compile() vor der Enthüllung. Die Overlay-Farbe im Shader mit #include <colorspace_fragment> ausgeben, sonst wirkt sie zu dunkel.",
   "mobil": "Leichtere Assets ausliefern (Geräteklasse erkennen, kleinere Texturen). Auf sehr schwachen Geräten oder ohne WebGL ein statisches Bild oder Video zeigen. Pixelratio begrenzen (höchstens 2).",
   "reduzierte_bewegung": "Kein Kameraflug. Die Szene per kurzem Fade (bis 300 ms) oder sofort zeigen.",
   "barrierefreiheit": "Balken mit role=progressbar und aria-valuenow. Textliche Beschreibung der Szeneninhalte. Ein Knopf „Ohne 3D ansehen“ bricht das Laden ab und zeigt ein Bild. Die übrige Seite (Kaufbox, Text) ist während des Ladens voll bedienbar."
  },
  "geprueft": "01.11.2026",
  "notiz": "Rezept d2c-produkt-zuerst (Tonwerk Olewig, Keramikleuchten aus Trier). Das Rezept schließt Preloader aus, erlaubt 3D aber als echten Produkt-Konfigurator. Genau dafür ist dieses Muster gedacht, deshalb deckt die Ladeszene nur die 3D-Bühne ab, Kaufbox und Seite sind sofort da. Die Dateien sind echt: Glasur-, Kabel- und Umgebungstexturen aus Canvas und Profildaten als Blob-URLs, geladen über TextureLoader/fetch und gemeldet über LoadingManager. Nur das Netz ist simuliert (Manifestgrößen, 1,9 MB/s), damit der Fortschritt sichtbar wird. Die Leuchte ist LatheGeometry mit unglasiertem Tonrand, PMREM-Umgebung aus Canvas. Ohne WebGL oder per „Ohne 3D ansehen“ erscheint eine SVG-Zeichnung. Die Vorführung wechselt Noise, Raster und Fade und klickt danach eine Glasur. Bei Software-Rendering (SwiftShader) laufen Pixelratio 0,75 und keine Kantenglättung. AD-Review 01.11.2026: Note 5. Zugang 02.11.2026: Pause-Knopf „Bewegung anhalten“ (button, aria-pressed, Pause-/Spiel-Zeichen) oben rechts neben „Neu laden“, ruft pause()/weiter(); Ziehen bleibt angehalten möglich (Orbit nutzt ungebremste Bildzeit), unter 560 px nur Zeichen, unter 360 px untereinander. Fokus: outline 2px statt outline none an Werkzeug-, Modus-, Glasur-, Größen- und Kaufknopf. Lader nach dem Ausblenden visibility hidden (Knopf „Ohne 3D ansehen“ nicht mehr unsichtbar fokussierbar), „Neu laden“ vor der Enthüllung ebenso. Kontrastmodus-Rahmen für Knöpfe."
 }
}
