← wszystkie wpisy
Poradniki

Scena three.js z niskim wynikiem w PageSpeed: dlaczego to nie JavaScript

Scena 3D chodzi u Ciebie płynnie, sześćdziesiąt klatek, zero zacięć. Wrzucasz stronę do PageSpeed Insights i dostajesz 48. Na czerwono świeci Total Blocking Time, a w rozbiciu wątku głównego wisi jedno długie zadanie podpięte pod gsap.min.js. Pierwsza myśl to „za dużo JavaScriptu" albo „za dużo cząstek". Obie są prawie zawsze błędne, a szukanie po nich potrafi zjeść cały dzień.

Ten wpis to diagnoza, nie recepta. Pokazuję, dlaczego ciężka scena three.js dostaje niski wynik w PageSpeed i gdzie naprawdę siedzi koszt. Jak to zabrzmi znajomo, na końcu jest link do gotowej metody.

PageSpeed nie ma karty graficznej

To jest klucz, od którego trzeba zacząć, bo zmienia całą resztę. Google nie mierzy Twojej strony na karcie graficznej. Lighthouse i PageSpeed uruchamiają bezgłowego Chrome, w którym WebGL idzie przez programowe rysowanie na procesorze (SwiftShader). Twoja scena, która na Twoim GPU liczy się w ułamku milisekundy, u Google liczy się na CPU.

Sprawdzisz to jednym odczytem nazwy renderera. Jeśli WEBGL_debug_renderer_info zwraca swiftshader, llvmpipe, softpipe albo software, to jest właśnie ten tryb. Tego samego doświadczają zresztą realni użytkownicy na maszynach wirtualnych i słabym sprzęcie. Dopóki mierzysz na własnej karcie, nie widzisz tego, co widzi Google.

Dlaczego to nie JavaScript

W rozbiciu pracy wątku głównego patrzysz na kategorie: „Script Evaluation", „Style & Layout", „Other". Przy ciężkiej scenie WebGL kategoria „Other" potrafi być większa niż „Script Evaluation". To nie jest worek bez znaczenia. Other to najczęściej czekanie na kartę graficzną i skanowanie pikseli, czyli praca, której profil skryptów w ogóle nie pokazuje.

Do tego dochodzi mylące nazewnictwo. Długie zadanie z etykietą gsap.min.js albo requestAnimationFrame to zwykle nie GSAP. W jednej klatce wszystkie wywołania requestAnimationFrame lądują w jednym zadaniu, które dostaje etykietę tego, kto je zaczął. Zegar GSAP rejestruje się pierwszy, więc bierze na siebie winę za cudzą pracę. Optymalizowanie GSAP w tym miejscu to strzelanie w zły cel.

Pierwsza klatka to najdroższa klatka

Scena z reguły rysuje na starcie jedną klatkę i czeka na ruch człowieka. Pomiar Google nie rusza myszą, więc widzi dokładnie tę jedną klatkę. A ta jedna klatka kompiluje wszystkie programy cieniowania naraz.

Na jednym z moich produkcyjnych wdrożeń scena miała czternaście programów: drobinki, łuki, kilka warstw poświaty, złożenie, wyjście. Każdy z nich kompilował się w pierwszej klatce, a biblioteka po każdej kompilacji pytała kartę o wynik. Każde takie pytanie czeka, aż karta przerobi całą kolejkę poleceń. Efekt: jedno zadanie 656 ms na maszynie Google i wynik 73 na komputerze. Total Blocking Time siedział w okolicach 700 ms. Nic z tego nie było JavaScriptem w sensie, w jakim myśli o nim większość poradników.

Cięcie cząstek nie pomaga, a psuje telefon

Najczęstszy odruch to zmniejszenie liczby cząstek „pod wydajność". To nieporozumienie na dwóch poziomach.

Po pierwsze, cząstki są tanie. Punkt to jeden wierzchołek. Drogie jest wypełnianie ekranu, czyli poświata, przezroczystość i nakładanie się warstw, a nie sama liczba punktów. Ucinając cząstki, tniesz tani zasób i zostawiasz drogi nietknięty.

Po drugie, obraz zbudowany z cząstek ma rozdzielczość równą ich liczbie. Napis z dziesięciu tysięcy drobinek wygląda inaczej niż ten sam napis z siedemdziesięciu tysięcy. Kiedy na telefonie dajesz dziesięć razy mniej cząstek „żeby było lżej", napis robi się rozmyty albo dziurawy, a i tak nie ruszasz prawdziwego kosztu. Na jednym wdrożeniu podniosłem liczbę cząstek na telefonie z siedmiu tysięcy do siedemdziesięciu dwóch tysięcy i napis wreszcie był ostry, a wynik się nie pogorszył, bo problem nigdy nie leżał w liczbie punktów.

Jak to zmierzyć, żeby nie gonić szumu

Zanim cokolwiek naprawisz, potrzebujesz miary, która nie kłamie. Tu są trzy pułapki, w które wpada prawie każdy.

Co robisz Dlaczego kłamie Co robić zamiast tego
Profil w DevTools na swojej karcie Drugie wczytanie ma programy z pamięci podręcznej dysku, kompilacja znika Świeży profil bez pamięci, rysowanie programowe (--disable-gpu)
Jeden pomiar PageSpeed Szum ±7 do 10 punktów, ten sam plik daje 80 i 95 Mediana z trzech, próg istotności 7 punktów
Patrzenie na kategorie zamiast na zadania „Other" nic Ci nie powie o przyczynie Czytaj listę długich zadań z czasem startu

Reguła jest prosta. Mierz w warunkach Google, nie na swojej karcie. Czytaj konkretne zadania, nie kategorie. Zmianę mniejszą niż siedem punktów traktuj jak przypadek, nie jak sukces. Dopiero z taką miarą ma sens dotykać sceny.

Co z tym zrobić

Naprawa istnieje i jest powtarzalna. W skrócie: pierwsza klatka nie może kompilować programów, więc kompilujesz je z wyprzedzeniem i rozgrzewasz w tle, we właściwych warunkach renderowania. Pętle po pikselach i budowa sceny nie mogą siedzieć w jednym zadaniu przy load. Telefon dostaje tę samą liczbę cząstek, lżejsze są tylko ujęcia bez napisu. Na tym samym wdrożeniu, na którym zaczynałem od 73, skończyłem na 97 na komputerze, z Total Blocking Time zbitym z 700 do 140 ms i pierwszą klatką z 656 ms do kilku milisekund, bez zmiany wyglądu sceny.

Całą metodę z gotowym kodem, protokołem pomiaru i skryptem do PageSpeed spakowałem jako gotowy skill dla programistów i agentów kodujących. Jeśli wolisz oddać robotę mnie, napisz i podeślij URL sceny - powiem wprost, gdzie siedzi koszt i ile realnie zdejmę. Ta sama wiedza o three.js stoi za moimi konfiguratorami produktowymi 3D, więc jeśli budujesz konfigurator i martwisz się o wynik PageSpeed, to jest dokładnie ten sam problem.

Często zadawane pytania

Czy niski wynik PageSpeed przy scenie WebGL szkodzi pozycji w Google?

Google rankinguje przede wszystkim po danych od realnych użytkowników (Core Web Vitals: LCP, CLS, INP), a nie po wyniku laboratoryjnym z PageSpeed. Sam <canvas> nie jest kandydatem na LCP, więc scena WebGL nie psuje LCP. Psuje za to Total Blocking Time, który jest miarą laboratoryjną, i realny INP, jeśli scena blokuje wątek podczas interakcji. Warto to naprawić dla użytkownika i dla wyniku, ale bez paniki, że jeden czerwony wskaźnik w PageSpeed od razu zrzuca stronę w wynikach.

Dlaczego długie zadanie jest podpisane jako GSAP, skoro nie używam GSAP intensywnie?

Bo etykieta długiego zadania wskazuje skrypt, który je zaczął, a nie ten, który wykonał pracę. Wszystkie wywołania requestAnimationFrame z jednej klatki idą w jednym zadaniu i dostają nazwę pierwszego z nich. Zegar GSAP rejestruje się wcześnie, więc bierze winę na siebie. Prawdziwym ciężarem jest zwykle kompilacja programów cieniowania w pierwszej klatce sceny. Nie optymalizuj GSAP na tej podstawie, bo naprawiasz nie ten element.

Czy wystarczy przenieść scenę do Web Workera albo OffscreenCanvas?

Zwykle nie i zwykle nie tędy droga jako pierwszy ruch. Web Worker i OffscreenCanvas to dzień pracy, a problem najczęściej rozwiązuje właściwa kolejność zadań i kompilacja programów z wyprzedzeniem, czyli zmiana na kilka godzin. Do Workera warto sięgać, kiedy naprawdę masz ciężką pracę CPU w pętli renderowania, a nie po to, żeby ukryć kompilację shaderów przed pomiarem.

Ile realnie da się ugrać na wyniku?

To zależy od sceny, kodu i hostingu, więc nie obiecuję liczby w ciemno. Na wdrożeniu, które opisuję, było to z 73 do 97 na komputerze i z 700 do 140 ms Total Blocking Time. Klucz to zdjęcie kompilacji z pierwszej klatki i uporządkowanie zadań przy starcie. Jeśli Twój niski wynik bierze się z zupełnie innego miejsca, na przykład z ciężkich obrazów albo z ładowania fontów, to osobna sprawa, którą też widać w pomiarze.

Czy da się to naprawić bez zmiany wyglądu sceny?

Tak i to jest cały sens. Metoda pilnuje, żeby obraz na komputerze został nietknięty co do procenta, a na telefonie był tak samo ostry, tylko mniejszy. Chodzi o kolejność i sposób rysowania, o to, kiedy kompilują się programy i co siedzi w pierwszej klatce, a nie o cięcie grafiki. Wynik podnosisz, a scena wygląda tak samo.

Masz projekt na oku?

Napisz - wycenę odeślę do 48 h. Bez agencji, bez pośredników.

✉ napisz: dominik.gronski@grodev.pl
Dominik Groński
Dominik Groński
Full-stack developer · GroDev · Poznań

Buduję custom systemy webowe: Laravel, WooCommerce, konfiguratory 3D w Three.js. Piszę bez ściemy, od strony biznesu który to potem utrzymuje. Prowadzę GroDev - studio jednoosobowe z Poznania.

Zadzwoń Darmowa wycena