Прискорення браузерних обчислень за допомогою WebAssembly
При ресайзі 100 зображень у 4K в браузері стандартний JS на Canvas видає 2 FPS — інтерфейс зависає. Після заміни на WebAssembly (WASM)-модуль на Rust отримуємо стабільні 60 FPS без блокування потоку рендерингу. На проєкті фоторедактора ми досягли прискорення в 10 разів, що дозволило клієнту заощадити до 30% на хмарних обчисленнях і скоротити витрати на серверні потужності до 40%. В іншому кейсі, з CAD-двигуном, заміна розрахунків на WASM скоротила час генерації креслення з 12 до 0,8 секунди.
WASM — бінарний формат інструкцій для віртуальної машини браузера. Він бере на себе код, де нативна швидкість критична: кодеки, криптографія, обробка зображень, фізичні двигуни, CAD, ML-інференс. WASM працює в ізольованому sandbox і викликається з JS як звичайна функція. Підтримка є у всіх сучасних браузерах — детальніше в WebAssembly | MDN.
Порівняння продуктивності: JS vs WASM на ресайзі 4K JPEG
| Метод | Час ресайзу | FPS | Розмір бінарника |
|---|---|---|---|
| Canvas 2D | 450 мс | 2.2 | 0 KB (браузерний) |
| WebAssembly (Rust) | 45 мс | 22 | 280 KB стиснутого |
| WebAssembly + Worker | 48 мс | 20 (UI не заблоковано) | 295 KB |
WASM-версія швидша в 10 разів для однієї операції і дозволяє основному потоку рендерингу дихати.
Чому варто обрати Rust для компіляції в WASM?
Rust — лідер за Developer Experience для WASM. Інструмент wasm-pack генерує біндинги автоматично, а wasm-bindgen підтримує складні типи (рядки, масиви) без ручної роботи з пам'яттю. Ми використовуємо Rust у 80% проєктів WASM. Приклад коду ресайзу зображення:
// src/lib.rs — приклад ресайзу зображення use wasm_bindgen::prelude::*; use image::{DynamicImage, ImageFormat}; use std::io::Cursor; #[wasm_bindgen] pub fn resize_image(data: &[u8], width: u32, height: u32) -> Vec<u8> { let img = image::load_from_memory(data).unwrap(); let resized = img.resize_exact(width, height, image::imageops::FilterType::Lanczos3); let mut output = Cursor::new(Vec::new()); resized.write_to(&mut output, ImageFormat::WebP).unwrap(); output.into_inner() } Команда wasm-pack build --target web --release збирає готовий до інтеграції модуль.
Як завантажити WASM без блокування інтерфейсу?
Важкі обчислення потрібно виносити в Web Worker. Ось мінімальна реалізація на TypeScript:
// wasm-worker.ts import init, { resize_image } from './pkg/image_processor'; let initialized = false; self.onmessage = async (event: MessageEvent) => { const { id, type, payload } = event.data; if (!initialized) { await init(); initialized = true; } if (type === 'RESIZE') { const { imageData, width, height } = payload; const result = resize_image(new Uint8Array(imageData), width, height); self.postMessage({ id, type: 'RESULT', payload: result.buffer }, [result.buffer]); } }; Передача buffer через Transferable виключає копіювання — дані переміщуються між потоками за O(1).
Які задачі найкраще підходять для WASM?
Окрім обробки зображень, WASM ефективний для:
- Криптографічні алгоритми (AES, хешування) — прискорення до 5×.
- Стиснення та розпакування даних (Zlib, Brotli) — зниження часу в 3–4 рази.
- Фізичні симуляції в іграх та CAD — стабільні 60 FPS.
- ML-інференс на клієнті — робота моделей прямо в браузері без відправки даних на сервер.
Порівняння підходів: Rust vs C++ для WASM
| Критерій | Rust (wasm-pack) | C++ (Emscripten) |
|---|---|---|
| Управління пам'яттю | Автоматичне (без GC) | Ручне (new/delete) |
| Генерація біндингів | wasm-bindgen | Embind |
| Розмір бінарника | ~200 KB (мінімальний) | ~400 KB (з runtime) |
| Швидкість компіляції | Швидка (LLVM) | Помірна |
Rust кращий для нових проєктів, C++ — для портування legacy-коду.
Що входить в роботу над впровадженням WASM?
- Аналіз вузьких місць в JS: Core Web Vitals, час виконання, обсяг даних.
- Вибір цільової мови (Rust, C/C++) або готового WASM-пакету.
- Компіляція та генерація біндингів (wasm-pack / Emscripten).
- Інтеграція через Web Worker з Transferable об'єктами.
- Оптимізація розміру бінарника: tree-shaking, LTO, налаштування кешування.
- Документація по збірці та розгортанню, доступ до репозиторію.
Процес роботи: від аналізу до деплою
- Аналітика — вивчаємо поточний код, вимірюємо продуктивність, визначаємо кандидатів на WASM.
- Проєктування — обираємо стек та архітектуру модуля (Worker + Transferable).
- Реалізація — пишемо код на Rust/C, компілюємо, тестуємо.
- Інтеграція — підключаємо модуль в проєкті, налаштовуємо HTTP-заголовки для SharedArrayBuffer при необхідності.
- Оптимізація та деплой — зменшуємо розмір бінарника, перевіряємо Core Web Vitals, викочуємо в продакшен.
Терміни: від 3 до 5 днів. Вартість розраховується індивідуально, але в середньому проєкт окупається за 2–3 місяці.
Типові помилки при роботі з WASM
- Забувають виставити заголовки
Cross-Origin-Embedder-Policy: require-corpтаCross-Origin-Opener-Policy: same-originдляSharedArrayBuffer. - Викликають WASM-функції в основному потоці — блокують UI. Потрібен Worker.
- Передають дані через копіювання замість Transferable — втрачають приріст швидкості.
Наш досвід: понад 10 років веб-розробки, 50+ проєктів з WASM. Гарантуємо оптимізацію Core Web Vitals та прискорення мінімум у 2 рази. Отримайте консультацію по вашому проєкту — напишіть нам. Також замовте аудит продуктивності вашого додатку.







