Podczas budowania symulacji przeglądarkowej jedną z pierwszych decyzji architektonicznych jest renderowanie: Canvas 2D czy WebGL? Odpowiedź zależy od liczby cząstek, częstotliwości aktualizacji, złożoności wizualnej i tego, ile czasu chcesz poświęcić na kod szablonowy. Po zbudowaniu ponad 1000 symulacji przy użyciu obu API, oto czego się nauczyliśmy — i kiedy każde podejście jest właściwym narzędziem.
Canvas 2D: prostota i szybka iteracja
API Canvas 2D jest naturalnym punktem wyjścia dla każdej symulacji przeglądarkowej. Udostępnia imperatywne API rysowania, które ściśle odpowiada temu, jak programiści naturalnie myślą o rysowaniu: przejdź do pozycji, narysuj kształt, wypełnij go kolorem. Bez kodu shaderów, buforów, bindowania. Działającą symulację cząstek można wyświetlić na ekranie w mniej niż 20 liniach:
Mocne strony Canvas 2D obejmują prosty, czytelny interfejs API z doskonałą dokumentacją MDN, wbudowane renderowanie tekstu (kluczowe do etykietowania osi i wartości w symulacjach), kompozycję obrazów, przycinanie ścieżek oraz wypełnienia gradientowe, które w WebGL wymagają złożonego kodu shaderów. Prototypowanie nowej symulacji w Canvas 2D zajmuje ułamek czasu potrzebnego w WebGL.
Fundamentalnym ograniczeniem jest to, że Canvas 2D jest ograniczony przez CPU. Każde wywołanie rysowania wykonuje się sekwencyjnie w jednym wątku procesora. Każde wywołanie ctx.arc() jest synchroniczne. GPU pozostaje bezczynne. Przy liczbie cząstek poniżej około 5000 rzadko ma to znaczenie — na dowolnym nowoczesnym urządzeniu z łatwością osiągniesz 60 fps. Przy 10 000 cząstek zazwyczaj spadniesz do 15–30 fps na sprzęcie średniej klasy. Przy 50 000 cząstek Canvas 2D staje się bezużyteczny dla wszystkiego, co przypomina interakcję w czasie rzeczywistym.
WebGL: moc GPU dla burz cząstek
WebGL udostępnia JavaScriptowi pełny potok GPU. Zamiast wywoływać polecenia rysowania jedno po drugim, przesyłasz wszystkie dane geometrii do pamięci GPU raz (lub raz na klatkę), a następnie wydajesz pojedyncze wywołanie rysowania, które wykonuje się w sposób masowo równoległy jednocześnie na tysiącach jednostek shaderowych GPU. Różnica wydajności dla symulacji obciążonych dużą liczbą cząstek nie jest przyrostowa — jest transformacyjna.
Rzeczywiste pomiary z naszych symulacji: symulacja płynu SPH z 50 000 cząstek działa w 60 fps w WebGL i 4 fps w Canvas 2D na tym samym sprzęcie. Symulacja galaktyki N ciał z 200 000 gwiazd jest interaktywna w WebGL i całkowicie bezużyteczna w Canvas 2D. To nie są przypadki brzegowe — reprezentują całe kategorie symulacji fizycznych, które są po prostu niemożliwe bez akceleracji GPU.
Minimalny shader wierzchołków WebGL, który pozycjonuje i koloruje cząstki, wygląda tak:
Koszt złożoności WebGL jest realny: mniej więcej 5–10 razy więcej kodu szablonowego niż odpowiednik w Canvas 2D. Musisz tworzyć i kompilować shadery, tworzyć i wiązać bufory, przesyłać dane do pamięci GPU za pomocą tablic typowanych, zarządzać lokalizacjami atrybutów oraz obsługiwać zdarzenia utraty kontekstu WebGL. Niestandardowe efekty wizualne, jak poświata, rozmycie ruchu i wizualizacja sił per-cząstka, wymagają nietrywialnej pracy z shaderami GLSL, którą Canvas 2D obsługuje za pomocą pojedynczego przypisania właściwości.
Schemat decyzyjny: co wybrać
W MySimulator stosujemy jasny zestaw kryteriów, rozpoczynając nową symulację:
Wybierz Canvas 2D, jeśli: liczba cząstek lub obiektów pozostaje poniżej 5000, potrzebujesz renderowania tekstu lub złożonych operacji na ścieżkach, efekty wizualne to proste wypełnienia i obrysy, prototypujesz i liczy się czas do działającego demo, lub symulację będą utrzymywać deweloperzy niezaznajomieni z API graficznymi.
Wybierz WebGL, jeśli: spodziewasz się ponad 10 000 jednoczesnych obiektów, potrzebujesz niestandardowych efektów wizualnych (poświata, smugi, gradienty kolorów per-cząstka na dużą skalę), renderujesz ciągłe pole (płyn, mapa cieplna, zbiór Mandelbrota), lub stabilność klatek na poziomie 60 fps jest niezbędna, by fizyka wyglądała poprawnie.
W zakresie 5000–10 000: przetestuj oba warianty. Canvas 2D razem z OffscreenCanvas i Web Workerami (przeniesienie pętli rysowania do wątku w tle) mogą znacznie zniwelować różnicę — podnosząc sufit Canvas 2D do mniej więcej 15 000 cząstek na nowoczesnym sprzęcie, zanim WebGL stanie się konieczny.
Inne opcje warte rozważenia: Three.js zapewnia znacznie wyższy poziom abstrakcji nad WebGL, co zmniejsza ilość kodu szablonowego kosztem pewnego zapasu wydajności. PixiJS jest stworzony specjalnie do renderowania sprite'ów i cząstek 2D i stanowi doskonałe rozwiązanie pośrednie. WebGPU, już dostępny w Chrome i Firefox za flagą, zastępuje WebGL nowoczesnym API — shadery obliczeniowe w WebGPU mogą wykonywać fizykę i renderowanie na GPU jednocześnie, czego WebGL nie potrafi zrobić natywnie.
W MySimulator podział jest konsekwentny: Boids, wahadło, podwójne wahadło, drapieżnik-ofiara, algorytmy sortowania, automaty komórkowe — wszystkie na Canvas 2D. Dynamika płynów (SPH), symulacja galaktyki N ciał, fajerwerki, fale oceaniczne, mgławica, zorza polarna, systemy cząstek z ponad 20 000 jednostkami — wszystkie na WebGL. Punkt przecięcia to około 8000 cząstek dla symulacji wymagającej fizyki przy 60 fps.
Praktyczne wskazówki migracyjne
Gdy symulacja Canvas 2D przerośnie swój renderer, ścieżka migracji jest czystsza, jeśli symulacja została napisana z rozdzieleniem fizyki i renderowania. Jeśli obliczasz pozycje cząstek w jednym miejscu, a rysujesz je w innym, zamiana kodu rysującego z Canvas 2D na WebGL jest prosta. Jeśli fizyka i renderowanie są przeplecione, będziesz musiał refaktoryzować oba jednocześnie.
Przed migracją najpierw profiluj. Otwórz Chrome DevTools, przełącz się na panel Performance i nagraj kilka sekund działania symulacji. Spójrz na wykres płomieniowy: czy wąskim gardłem jest funkcja aktualizacji fizyki (długie bloki JavaScript), czy wywołania renderowania (długie bloki paint i composite)? Jeśli wąskim gardłem jest fizyka, przejście na WebGL nie pomoże — musisz zoptymalizować algorytm aktualizacji, być może stosując haszowanie przestrzenne dla zapytań o sąsiedztwo zamiast porównań par O(n²). Jeśli wąskim gardłem jest renderowanie, WebGL pomoże drastycznie.
Dla symulacji Canvas 2D, w których fizyka nadaje się do obliczeń na GPU, rozważ transform feedback — technikę WebGL, w której wynik shadera wierzchołków jest zapisywany z powrotem do bufora zamiast być renderowany. Przenosi to samą symulację fizyczną na GPU, pozostawiając JavaScriptowi jedynie aktualizację jednej lub dwóch zmiennych uniform na klatkę. Właśnie w ten sposób nasze najbardziej wymagające symulacje WebGL osiągają swoją wydajność. Shadery obliczeniowe WebGPU czynią ten wzorzec jeszcze czystszym i stanowią kolejną granicę dla symulacji przeglądarkowych przy naprawdę ekstremalnej liczbie cząstek.