Справжнє питання
Коли ви починаєте нову симуляцію, вибір між Canvas 2D і Three.js рідко зводиться до питання «що краще». Йдеться про те, що насправді потрібно самій симуляції. 2D-API Canvas 2D напрочуд потужний, а потік рендерингу залишається на CPU — а це саме те, що часом потрібно, коли ваша фізика теж працює на CPU і вам потрібна тісна синхронність.
Three.js передає рендеринг на GPU через WebGL. Це правильний вибір, коли у вас десятки тисяч частинок, об'ємні ефекти або 3D-геометрія в реальному часі. Але це має свою ціну: більше коду, більша залежність і вищі витрати на налагодження.
Пряме порівняння
| Критерій | Canvas 2D | Three.js (WebGL) |
|---|---|---|
| Складність налаштування | 3 рядки коду | щонайменше ~20 рядків |
| Кількість частинок (60 FPS) | ~2 000–5 000 | 50 000–1 000 000+ |
| Розмір залежності | 0 КБ (вбудовано в браузер) | ~150 КБ (ядро Three.js) |
| Власні шейдери (GLSL) | Недоступно | Повна підтримка GLSL |
| 3D-рендеринг | Ручна проєкційна математика | Нативний 3D-граф сцени |
| ImageData / робота з пікселями | Швидкий, прямий доступ | Потрібне зчитування render target |
| Рендеринг тексту | fillText() — тривіально | TextGeometry / Sprites — складно |
| Інтерактивність (клік/перетягування) | Проста координатна математика | Потрібен рейкастинг |
| Налагодження | DevTools, брейкпоінти працюють скрізь | Помилки GLSL важко читати |
| Продуктивність на мобільних | Прийнятно для малого N | GPU добре справляється з великим N |
| Постобробка (bloom, blur) | Вручну, повільно | EffectComposer + вбудовані проходи |
Коли перемагає Canvas 2D
Обирайте Canvas 2D, коли:
- Симуляція за своєю природою 2D. Клітинні автомати, агентні моделі, 2D-гідродинаміка, графи, діаграми, дерева рішень — жодне з цього не виграє від 3D-графу сцени. Додавання Three.js стало б чистим накладним видатком.
-
Потрібен піксельний контроль. Патерни
реакції-дифузії, теплові карти та будь-яка симуляція, що пише в
піксельну сітку, найпростіше реалізуються через
ImageData. ОновлюватиUint8ClampedArrayна CPU й викликатиputImageData()простіше, ніж керувати текстурами WebGL. - Кількість частинок менша за ~5 000. Canvas 2D комфортно відображає 2 000–5 000 кіл або ліній при 60 FPS на середньому обладнанні. Якщо ваша симуляція ніколи не перевищує цю межу, не додавайте складність WebGL.
- Сторінка має завантажуватися швидко. Жодного тега скрипту з CDN, жодної ініціалізації WebGL-контексту. Симуляція на Canvas 2D може стати повністю інтерактивною вже через 50 мс після завантаження сторінки.
- Ви пишете швидкий прототип. Canvas 2D дозволяє ітерувати за лічені хвилини. Спершу перевірте алгоритм, потім оновлюйте рендерер.
Коли перемагає Three.js
Переходьте на Three.js, коли:
-
Кількість частинок перевищує ~5 000.
Використовуйте
PointsізBufferGeometryі власнимShaderMaterial. Переміщення мільйона частинок за кадр на GPU — тривіальна задача; Canvas 2D впав би до однозначних FPS. - Сцена справді 3D. Симуляції галактик, орбітальна механіка, молекулярна візуалізація та інженерні розрахунки потребують повноцінної 3D-камери, перспективної проєкції та буфера глибини.
- Потрібні власні пофрагментні ефекти. Хвилі Герстнера в океані, відбиття Френеля, рейтрейсинг на основі SDF, об'ємні хмари — усе це потребує фрагментних GLSL-шейдерів, які просто не можуть виконуватися в Canvas 2D.
-
Візуал вимагає освітлення чи тіней у реальному часі.
MeshStandardMaterialі карти тіней у Three.js працюють «з коробки». Відтворити навіть базове освітлення Фонга в Canvas 2D — це вже дослідницький проєкт.
Реальні приклади з цього сайту
З 225 симуляцій на цьому сайті приблизно половина використовує Canvas 2D, а половина — Three.js. Ось як розподілено:
- Canvas 2D: усі клітинні автомати (Гра «Життя», Лісова пожежа, Реакція-дифузія), усі 2D-агентні моделі (Boids 2D, мурахи, епідемія SIR), алгоритми сортування, дерева рішень, цифрові фільтри, фінансові графіки, моделі трафіку та більшість навчальних фізичних демонстрацій.
- Three.js: Галактика (80 000 зіркових частинок), шейдер океану (Герстнер + Френель), тектонічні плити (карта зміщення), SPH-гідродинаміка (100 000 частинок), path tracing, рейтрейсинг та усі справжні 3D-механічні симуляції.
Хороша евристика: починайте кожну симуляцію з Canvas 2D. Якщо після робочого прототипу ви впираєтеся в стелю частоти кадрів або потребуєте 3D-вигляду — мігруйте на Three.js. Так ви додасте зайву складність лише тоді, коли вона справді потрібна — і зрозумієте алгоритм, перш ніж боротися з рендерером.
Починаємо з Canvas 2D, мігруємо пізніше
Шлях міграції з Canvas 2D на Three.js зазвичай простий, якщо ви від самого початку відокремлюєте логіку симуляції від рендерингу. Тримайте оновлення фізики в чистій функції, що приймає стан і повертає новий стан. Рендерер — лише споживач цього стану.
// Хороша структура — рендерер можна замінити
function updatePhysics(state, dt) { /* чиста логіка */ return newState; }
function renderCanvas2D(ctx, state) { /* малює на канвасі */ }
function renderThreeJS(scene, state) { /* оновлює об'єкти Three.js */ }
// У циклі rAF:
state = updatePhysics(state, dt);
renderCanvas2D(ctx, state); // заміните на renderThreeJS, коли будете готові
Пастка, якої слід уникати: змішування фізичних розрахунків із
викликами малювання. Щойно ви робите
ctx.moveTo(particle.x += vx * dt, ...) всередині циклу
малювання, міграція перетворюється на повне переписування замість
простої заміни рендерера.
Чек-лист для рішення
- ⬜ Симуляція за природою 2D? → Canvas 2D
- ⬜ Кількість частинок < 5 000? → Canvas 2D
-
⬜ Потрібен піксельний доступ через
ImageData? → Canvas 2D - ⬜ Спочатку створюєте прототип? → Canvas 2D
- ⬜ Сцена справді 3D? → Three.js
- ⬜ Кількість частинок > 10 000? → Three.js
- ⬜ Потрібні власні GLSL-шейдери? → Three.js
- ⬜ Потрібне освітлення / тіні в реальному часі? → Three.js
- ⬜ Потрібна постобробка (bloom, SSAO)? → Three.js