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.
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.
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.
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.
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.
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:
- Wszędzie tablice typowane. Float32Array dla pozycji i prędkości. Brak zacinania się przez garbage collector w środku klatki.
- Stały krok czasowy z interpolacją. Fizyka działa wewnętrznie ze stałą częstotliwością 60 Hz. Gdy liczba klatek spada, wykonujemy pod-kroki i interpolujemy pozycje renderowania — bez artefaktów symulacji.
- InstancedMesh dla cząstek. 1000 cząstek jako jedno wywołanie rysowania. Bez instancingu było to 1000 osobnych wywołań — 50-krotny narzut GPU.
- Haszowanie przestrzenne dla par kolizji. Wykrywanie kolizji w fazie zgrubnej w O(n) zamiast O(n²). Kluczowe powyżej ~200 obiektów.
- Wczesne wycinanie. Symulacje sprawdzają document.hidden i wstrzymują pętlę fizyki. Karty w tle nie wykonują żadnej pracy.
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.