300 symulacji — czego się nauczyliśmy, budując największą bibliotekę fizyki WebGL

Dwa i pół roku, 300 symulacji, 78 kategorii, dwa języki. Oto co poszło dobrze, co zrobilibyśmy inaczej, pięć najtrudniejszych symulacji, jakie kiedykolwiek wypuściliśmy, oraz decyzje architektoniczne, które napędzały wszystko.

300
interaktywnych symulacji
78
kategorii STEM
2,5
roku łącznie

5 najtrudniejszych symulacji, jakie wypuściliśmy

Większość symulacji zajmuje dzień lub dwa: układ równań różniczkowych, pętla canvas, kilka suwaków. Te pięć zajęło ponad tydzień każda i zmusiło nas do przemyślenia całego podejścia.

#1

Projektant mostów (MES)

Składanie globalnej macierzy sztywności z dowolnych elementów belkowych, rozwiązywanie rzadkiego układu Ax = b metodą eliminacji Gaussa w JavaScript i wizualizacja naprężeń w kolorze w czasie rzeczywistym. Najtrudniejsza część: utrzymanie ważności grafu topologii, gdy użytkownik przeciąga węzły.

#2

Przepływ metodą Lattice-Boltzmann

Uzyskanie numerycznej stabilności operatora kolizji D2Q9 BGK przy zachowaniu wysokich liczb Reynoldsa. Warunki brzegowe Zou-He dla prędkości wlotowej okazały się zaskakująco nietrywialne, a krok strumieniowania wymaga układu pamięci przyjaznego dla pamięci podręcznej — zamiany dwóch płaskich tablic zamiast tablicy 3D.

#3

Akustyka sali koncertowej (FDTD)

Uruchomienie 2D akustycznego solvera różnic skończonych w dziedzinie czasu w wątku roboczym, przesyłanie migawek pola ciśnienia przez SharedArrayBuffer i renderowanie czoła fali jako gradientu kolorów przy 60 FPS — podczas gdy wątek UI pozostaje całkowicie wolny.

#4

Zwijanie białka (Monte Carlo)

Zwijanie w modelu sieciowym HP z symulowanym wyżarzaniem. Kombinatoryczna eksplozja konformacji wymaga starannie dobranego harmonogramu wyżarzania — schłodzisz za szybko i utkniesz w minimum lokalnym, za wolno i symulacja nigdy nie zbiegnie się w przeglądarce.

#5

Symulator obwodów kwantowych

Utrzymanie 2ⁿ-elementowego zespolonego wektora stanu w JavaScriptowej Float64Array, stosowanie bramek unitarnych jako iloczynów macierz-wektor i zachowanie responsywności UI dla maksymalnie 10 kubitów (1024 amplitudy). Serializacja bramek jako opis obwodu w formacie JSON okazała się późną, ale kluczową decyzją projektową.

Lekcje architektoniczne

✅ Co zadziałało: jeden plik na symulację

Każda symulacja to pojedynczy, samowystarczalny plik HTML. Brak etapu budowania, brak importów, brak bundlera. Wdrożenie nowej symulacji to pojedynczy git push. Czytelnicy mogą wyświetlić źródło i wszystko zrozumieć.

✅ Co zadziałało: wstrzykiwanie wspólnych komponentów

Niewielki plik components.js wstrzykuje pasek nawigacji, stopkę i przełącznik motywu do każdej strony w czasie działania. 300 plików dzieli jedną definicję nawigacji. Zmieniliśmy nawigację trzy razy — za każdym razem zajęło to 30 sekund.

⚠️ Żal: skopiowane CSS

Wczesne symulacje mają własne, autorskie CSS. Nowsze korzystają ze wspólnych zmiennych motywu. Mamy teraz ~50 symulacji lekko niespójnych pod względem rozmiarów czcionek i odstępów. Naprawa jest prosta, ale żmudna.

⚠️ Żal: brak schematu danych symulacji

Plik simulations.json rósł organicznie. Mamy trzy różne konwencje dla slugów kategorii, dwie dla tagów algorytmów i jedną symulację z literówką w ID, która rozprzestrzeniła się wszędzie. Walidator schematu od pierwszego dnia kosztowałby 30 minut.

Decyzja, która zmieniła wszystko: przeniesienie pętli fizyki do Web Workerów. Przed Workerami każda symulacja wykonująca ponad ~10 000 operacji na klatkę spychała UI poniżej 60 FPS na urządzeniach mobilnych. Wątki Workerów pozwalają fizyce działać niezależnie, przekazując pozycje do wątku głównego przez przekazywalne ArrayBuffery. Wątek główny tylko renderuje.

Wydajność: jak osiągnęliśmy 60 FPS na urządzeniach mobilnych

Każda symulacja działająca przy 60 FPS na telefonie ze średniej półki ma co najmniej trzy z tych pięciu cech:

Co dalej: droga do 500

Kolejne 200 symulacji skupi się na obszarach, które zaniedbaliśmy: systemy biologiczne (szczegółowa genomika, modele neuronauki), zaawansowane materiałoznawstwo (dynamika molekularna, DFT-lite), geofizyka (propagacja fal sejsmicznych, konwekcja płaszcza przy wyższej rozdzielczości) oraz tryb edukacyjny/dla dzieci z uproszczonymi kontrolkami i kierowanym odkrywaniem.

Przenosimy też najcięższe symulacje na shadery obliczeniowe WebGPU — solver lattice-Boltzmann, płyn SPH i akustykę FDTD. Shadery obliczeniowe działają 10–50 razy szybciej niż odpowiednik w JavaScript, co oznacza, że w końcu możemy robić 3D CFD w czasie rzeczywistym.