Реализация 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 generated bindings
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 код с нуля.