5 найскладніших симуляцій, які ми випустили
Більшість симуляцій займають день-два: система звичайних диференціальних рівнянь, цикл рендерингу на canvas, кілька повзунків. Ці п'ять забрали більше тижня кожна і змусили нас переосмислити весь підхід.
Конструктор мостів (МСЕ)
Збирання глобальної матриці жорсткості з довільних балкових елементів, розв'язання розрідженої системи Ax = b методом Гаусса на JavaScript та візуалізація напружень кольором у реальному часі. Найскладніше — забезпечити, щоб граф топології лишався коректним, поки користувач перетягує вузли.
Потік за методом ґратчастих рівнянь Больцмана
Забезпечення числової стабільності оператора зіткнень D2Q9 BGK при збереженні високих чисел Рейнольдса. Граничні умови Цзоу-Хе для швидкості на вході виявились несподівано нетривіальними, а крок «розповсюдження» вимагає кеш-дружнього розташування пам'яті — обмін двома плоскими масивами замість 3D-масиву.
Акустика концертного залу (FDTD)
Запуск 2D акустичного розв'язувача методом кінцевих різниць у часовій області у воркер-потоці, потокова передача знімків поля тиску через SharedArrayBuffer та рендеринг фронту хвилі у вигляді кольорового градієнта на 60 FPS — при цьому потік інтерфейсу залишається повністю вільним.
Згортання білка (метод Монте-Карло)
Згортання за HP-ґратковою моделлю методом імітаційного відпалу. Комбінаторний вибух конформацій вимагає ретельного графіка відпалу — надто швидке охолодження застрягає в локальних мінімумах, надто повільне — ніколи не сходиться в браузері.
Симулятор квантових схем
Підтримка комплексного вектора стану розміром 2ⁿ у JavaScript-масиві Float64Array, застосування унітарних вентилів як матрично-векторних добутків та збереження чуйності інтерфейсу для до 10 кубітів (1024 амплітуди). Серіалізація вентилів у вигляді JSON-опису схеми була пізнім, але вирішальним архітектурним рішенням.
Архітектурні уроки
✅ Що спрацювало: один файл на симуляцію
Кожна симуляція — це один самодостатній HTML-файл. Жодного етапу збирання, жодних імпортів, жодного бандлера. Розгортання нової симуляції — це один git push. Читачі можуть переглянути вихідний код і зрозуміти все.
✅ Що спрацювало: спільне впровадження компонентів
Крихітний components.js впроваджує навігацію, футер і перемикач теми на кожну сторінку під час виконання. 300 файлів використовують одне визначення навігації. Ми змінювали навігацію тричі — щоразу це займало 30 секунд.
⚠️ Шкодуємо: копіювання CSS
Ранні симуляції мають власний фірмовий CSS. Пізніші симуляції використовують спільні змінні теми. Зараз у нас близько 50 симуляцій із незначними розбіжностями у розмірах шрифтів та відступах. Виправлення просте, але нудне.
⚠️ Шкодуємо: відсутність схеми даних симуляцій
simulations.json ріс органічно. У нас три різні конвенції для слагів категорій, дві — для тегів алгоритмів, і одна симуляція з опискою в ID, яка розповсюдилась усюди. Валідатор схеми з першого дня коштував би 30 хвилин.
Рішення, що змінило все: перенесення фізичних циклів у Web Workers. До воркерів будь-яка симуляція, що виконувала більше ~10 000 операцій на кадр, опускала інтерфейс нижче 60 FPS на мобільних пристроях. Потоки-воркери дозволяють фізиці працювати незалежно, передаючи позиції в основний потік через передавані ArrayBuffer. Основний потік лише рендерить.
Продуктивність: як ми досягли 60 FPS на мобільних пристроях
Кожна симуляція, що працює на 60 FPS на середньому Android-пристрої, має щонайменше три з цих п'яти властивостей:
- Типізовані масиви всюди. Float32Array для позицій і швидкостей. Жодного тремтіння через збирання сміття посеред кадру.
- Фіксований крок часу з інтерполяцією. Фізика працює внутрішньо з фіксованою частотою 60 Гц. Якщо частота кадрів падає, ми виконуємо підкроки та інтерполюємо позиції рендерингу — жодних артефактів симуляції.
- InstancedMesh для частинок. 1000 частинок як один виклик малювання. Без інстансування це було 1000 окремих викликів малювання — 50-кратне навантаження на GPU.
- Просторове хешування для пар зіткнень. Широкофазне виявлення зіткнень за O(n) замість O(n²). Критично важливо для понад ~200 об'єктів.
- Раннє відсікання. Симуляції перевіряють document.hidden і зупиняють фізичний цикл. Фонові вкладки не виконують жодної роботи.
Що далі: шлях до 500
Наступні 200 симуляцій зосередяться на напрямках, яким ми приділяли недостатньо уваги: біологічні системи (детальна геноміка, моделі нейронауки), передове матеріалознавство (молекулярна динаміка, спрощена ТФГ), геофізика (поширення сейсмічних хвиль, конвекція мантії з вищою роздільністю) та освітній/дитячий режим зі спрощеним керуванням і керованим відкриттям.
Ми також переносимо найважчі симуляції на обчислювальні шейдери WebGPU — розв'язувач методом ґратчастих рівнянь Больцмана, SPH-рідину та акустику FDTD. Обчислювальні шейдери працюють у 10–50 разів швидше за еквівалентний JavaScript-код, а це означає, що ми нарешті зможемо виконувати 3D CFD у реальному часі.