Реалізація WebAssembly (WASM) модулів для вебзастосунку

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація WebAssembly (WASM) модулів для вебзастосунку
Складна
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Впровадження WebAssembly (WASM) модулів для веб-додатку

WebAssembly — двійковий формат інструкцій для віртуальної машини, вбудованої у браузер. Не заміна JavaScript — доповнення для завдань, де нативна швидкість критична: кодеки, криптографія, обробка зображень, фізичні рушії, ML-інференс, CAD-інструменти, емулятори.

WASM працює в ізольованій sandbox, розділяє пам'ять з JS через WebAssembly.Memory (лінійний буфер ArrayBuffer), викликається з JS як звичайна функція.

Три способи отримати WASM-модуль

1. Компіляція з Rust (переважно)

Rust + wasm-pack — найкращий досвід розробки сьогодні. Хороша типізація, автоматичне біндування через wasm-bindgen, підтримка складних типів.

cargo new --lib image-processor
cd image-processor
cargo add wasm-bindgen image
// 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_bindgen]
pub fn grayscale(data: &[u8]) -> Vec<u8> {
    let img = image::load_from_memory(data).unwrap();
    let gray = img.grayscale();

    let mut output = Cursor::new(Vec::new());
    gray.write_to(&mut output, ImageFormat::Png).unwrap();
    output.into_inner()
}
wasm-pack build --target web --release

2. Компіляція з C/C++ через Emscripten

Для портування існуючих C-бібліотек:

emcc processing.c \
  -O3 \
  -o processing.js \
  -s WASM=1 \
  -s EXPORTED_FUNCTIONS='["_process_data"]' \
  -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \
  -s ALLOW_MEMORY_GROWTH=1

3. Готові WASM-пакети

Багато бібліотек уже поставляють WASM: @ffmpeg/ffmpeg, @sqlite.org/sqlite-wasm, pdfium-wasm, @mlc-ai/web-llm.

Завантаження та ініціалізація

// wasm-loader.ts
interface WasmModule {
  resize_image: (data: Uint8Array, width: number, height: number) => Uint8Array
  grayscale: (data: Uint8Array) => Uint8Array
  memory: WebAssembly.Memory
}

let moduleInstance: WasmModule | null = null

async function loadWasmModule(): Promise<WasmModule> {
  if (moduleInstance) return moduleInstance

  // Streaming compilation — швидше ніж ArrayBuffer
  const response = await fetch('/wasm/image-processor.wasm')
  const { instance } = await WebAssembly.instantiateStreaming(response, {
    env: {
      memory: new WebAssembly.Memory({ initial: 256, maximum: 4096 }),
    },
  })

  moduleInstance = instance.exports as unknown as WasmModule
  return moduleInstance
}

// Або через wasm-pack згенеровані біндинги
async function loadRustWasm() {
  const { default: init, resize_image, grayscale } = await import('./pkg/image_processor')
  await init() // завантажує та ініціалізує WASM
  return { resize_image, grayscale }
}

Передача даних: JS ↔ WASM

WASM працює тільки з числами (i32, i64, f32, f64). Строки та масиви потребують роботи з пам'яттю:

// Ручна робота з пам'яттю (без wasm-bindgen)
async function callWasmWithData(
  wasmModule: WebAssembly.Instance,
  inputData: Uint8Array
): Promise<Uint8Array> {
  const exports = wasmModule.exports as {
    alloc: (size: number) => number
    dealloc: (ptr: number, size: number) => void
    process: (ptr: number, len: number) => number
    memory: WebAssembly.Memory
  }

  const memory = new Uint8Array(exports.memory.buffer)

  // Виділити пам'ять у WASM та скопіювати дані
  const inputPtr = exports.alloc(inputData.length)
  memory.set(inputData, inputPtr)

  // Викликати функцію — вона повертає вказівник на результат
  const resultPtr = exports.process(inputPtr, inputData.length)

  // Прочитати довжину результату (перші 4 байти — конвенція)
  const resultLen = new DataView(exports.memory.buffer).getUint32(resultPtr, true)
  const result = new Uint8Array(exports.memory.buffer, resultPtr + 4, resultLen).slice()

  // Звільнити пам'ять
  exports.dealloc(inputPtr, inputData.length)
  exports.dealloc(resultPtr, resultLen + 4)

  return result
}

З wasm-pack/wasm-bindgen цей boilerplate генерується автоматично.

Запуск WASM у Web Worker

Важкі WASM-операції варто виносити в Worker, інакше main thread блокується:

// 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
  }

  try {
    switch (type) {
      case 'RESIZE': {
        const { imageData, width, height } = payload
        const result = resize_image(new Uint8Array(imageData), width, height)
        // Transferable — без копіювання
        self.postMessage(
          { id, type: 'RESULT', payload: result.buffer },
          [result.buffer]
        )
        break
      }
    }
  } catch (error) {
    self.postMessage({ id, type: 'ERROR', payload: (error as Error).message })
  }
}
// main.ts — використання
const worker = new Worker(new URL('./wasm-worker.ts', import.meta.url), {
  type: 'module',
})

async function resizeImage(file: File, width: number, height: number): Promise<Blob> {
  const buffer = await file.arrayBuffer()

  return new Promise((resolve, reject) => {
    const id = Math.random().toString(36).slice(2)

    const handler = (event: MessageEvent) => {
      if (event.data.id !== id) return
      worker.removeEventListener('message', handler)

      if (event.data.type === 'ERROR') {
        reject(new Error(event.data.payload))
      } else {
        resolve(new Blob([event.data.payload], { type: 'image/webp' }))
      }
    }

    worker.addEventListener('message', handler)
    // Transferable — передати буфер без копіювання
    worker.postMessage({ id, type: 'RESIZE', payload: { imageData: buffer, width, height } }, [buffer])
  })
}

Приклад: SQLite у браузері

import { createDbWorker } from 'sql.js-httpvfs'

async function initDatabase() {
  const worker = await createDbWorker(
    [{ from: 'inline', config: { serverMode: 'full', url: '/db/products.sqlite3', requestChunkSize: 4096 } }],
    '/sqlite.worker.js',
    '/sql-wasm.wasm'
  )

  const results = await worker.db.query(
    'SELECT id, name, price FROM products WHERE category = ? ORDER BY price LIMIT 20',
    ['electronics']
  )

  return results
}

Оптимізації завантаження

<!-- Передзавантажити WASM -->
<link rel="preload" href="/wasm/processor.wasm" as="fetch" type="application/wasm" crossorigin>
// Кешувати скомпільований модуль через Cache API
async function loadWithCache(url: string): Promise<WebAssembly.Module> {
  const cache = await caches.open('wasm-modules-v1')
  const cached = await cache.match(url)

  if (cached) {
    const buffer = await cached.arrayBuffer()
    return WebAssembly.compile(buffer)
  }

  const response = await fetch(url)
  await cache.put(url, response.clone())
  return WebAssembly.compileStreaming(response)
}

Що включено

Вибір мови компіляції (Rust/C/готовий пакет) під завдання, настройка сборки wasm-pack або Emscripten, реалізація JS-біндингів, інтеграція з Web Worker для неблокуючого виконання, настройка HTTP-заголовків (Content-Type: application/wasm, COEP/COOP для SharedArrayBuffer), оптимізація розміру бінарника.

WASM-бінарники потребують додаткових HTTP-заголовків для SharedArrayBuffer:

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

Терміни: 3–5 днів залежно від складності алгоритму та необхідності писати Rust/C код з нуля.