WithCodeMedia-1-pc
previous arrowprevious arrow
next arrownext arrow

WithCodeMedia-1-sp
previous arrowprevious arrow
next arrownext arrow

WebAssembly(Wasm)で画像処理ライブラリをフロントエンドで高速化する方法【完全ガイド】|Rust・wasm-pack・JavaScript連携を徹底解説

目次

この記事でわかること

  • WebAssemblyとは何か・JavaScriptと比べて2〜4倍高速になる仕組み
  • Rust + wasm-packの環境構築手順(インストールからビルドまで)
  • グレースケール・セピア・エッジ検出など画像処理関数のRust実装コード全文
  • Vite + Reactへの統合方法とWeb WorkersとWasmの組み合わせパターン
  • ffmpeg.wasm・OpenCV.jsなど既存Wasmライブラリの活用方法とエラー対処法
生徒

ブラウザ上でリアルタイム画像フィルターを実装したいんですが、JavaScriptだとフレームレートが全然出なくて……

ペン博士

WebAssembly(Wasm)を使えばそれが解決できるんじゃ!JavaScriptより2〜4倍高速に動作するWasmをRustで書いて、JavaScriptから呼び出す仕組みを作れば、ブラウザ上でネイティブアプリに近いパフォーマンスが実現できるぞ。wasm-packを使えば環境構築もシンプルじゃ!

結論:WebAssemblyを使えば、ブラウザ上でJavaScriptの2〜4倍高速な画像処理が実現できます。動画編集ツール・画像フィルター・物理シミュレーション・暗号化処理などJavaScriptだけでは速度の限界を感じる場面で特に有効です。

本記事では、WebAssemblyの概要・Rustとwasm-packによる開発環境構築・画像処理の実装例(グレースケール・セピア・エッジ検出・明るさ調整)・Web WorkersとWasmの組み合わせ・Vite/Reactへの統合・メモリ管理を完全解説します。


WebAssemblyとは|JavaScriptと何が違うのか

WebAssembly(Wasm)は2017年にW3Cで標準化された低レベルバイナリフォーマットです。C/C++・Rust・Goなどで書かれたコードをコンパイルしてブラウザ上で実行できます。Chrome・Firefox・Safari・Edgeのすべての主要ブラウザが対応しており、2026年現在ではほぼ全ブラウザで利用可能です。

WebAssemblyが高速な理由は2点あります。バイナリ形式なのでJavaScriptのようなテキストパースが不要で読み込みが速い点、および静的型付けなのでJITコンパイラの推測が不要で最適化されたコードが生成される点です。特に整数・浮動小数点演算が多い画像処理では顕著な差が出ます。

WebAssemblyの仕組みとJavaScriptとの比較図
>【JavaScript vs WebAssembly の比較】

項目              | JavaScript          | WebAssembly
-----------------|--------------------|------------------
実行形式          | テキスト(JIT最適化) | バイナリ(事前最適化済み)
速度             | 相対速度 1.0        | 相対速度 2〜4倍
型付け           | 動的型付け           | 静的型付け
DOM操作          | ✅ 可能              | ❌ 不可(JSを経由)
メモリモデル      | ガベージコレクション  | 線形メモリ(手動または RAII)
使い所           | UI・ロジック全般     | 計算集約型の処理
バンドルサイズ    | ソースのまま         | コンパクトなバイナリ(gz済みで10〜100KB程度)

WebAssemblyのパフォーマンスベンチマーク

タスクJavaScript (ms)WebAssembly (ms)高速化倍率
大量配列のソート(100万件)120452.7倍
画像処理(グレースケール変換 4K画像)200702.9倍
物理演算シミュレーション3001003.0倍
暗号化処理(SHA-256 10MB)180553.3倍
Sobel エッジ検出(FHD画像)4501303.5倍

上記ベンチマークはChrome 122、Apple M2チップでの計測値です。デバイスやブラウザによって差はありますが、計算集約型の処理では一貫してWebAssemblyが優位です。


開発環境構築|Rust + wasm-pack のセットアップ

# 1. Rust のインストール
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env

# 2. wasm-pack のインストール(Rust → WebAssembly コンパイラ)
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

# 3. wasm-bindgen ターゲットを追加
rustup target add wasm32-unknown-unknown

# バージョン確認
rustc --version   # rustc 1.82.0 以上
wasm-pack --version  # wasm-pack 0.13 以上
# 4. プロジェクトを作成
cargo new --lib wasm-image-processor
cd wasm-image-processor
# Cargo.toml - 依存関係の設定
[package]
name = "wasm-image-processor"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]  # WebAssemblyにコンパイルするには cdylib が必要

[dependencies]
wasm-bindgen = "0.2"  # JavaScriptとのバインディング生成

[profile.release]
opt-level = 3       # 最大最適化
lto = true          # Link Time Optimization(バイナリサイズ削減)
codegen-units = 1   # コード生成単位を1に(さらなる最適化)

実装コード全文|画像処理関数をRustで書く

src/lib.rs:画像処理関数の実装

// src/lib.rs

use wasm_bindgen::prelude::*;

/// グレースケール変換
/// Canvas の ImageData(RGBA 配列)を受け取ってグレースケールに変換する
#[wasm_bindgen]
pub fn apply_grayscale(image_data: &mut [u8]) {
    for i in (0..image_data.len()).step_by(4) {
        let r = image_data[i] as f32;
        let g = image_data[i + 1] as f32;
        let b = image_data[i + 2] as f32;
        // 輝度計算(NTSC係数:人間の目の感度に合わせた重み付け)
        let gray = (r * 0.299 + g * 0.587 + b * 0.114) as u8;
        image_data[i] = gray;
        image_data[i + 1] = gray;
        image_data[i + 2] = gray;
        // アルファ値 (image_data[i + 3]) はそのまま
    }
}

/// セピア変換
#[wasm_bindgen]
pub fn apply_sepia(image_data: &mut [u8]) {
    for i in (0..image_data.len()).step_by(4) {
        let r = image_data[i] as f32;
        let g = image_data[i + 1] as f32;
        let b = image_data[i + 2] as f32;
        image_data[i] = ((r * 0.393 + g * 0.769 + b * 0.189) as u8).min(255);
        image_data[i + 1] = ((r * 0.349 + g * 0.686 + b * 0.168) as u8).min(255);
        image_data[i + 2] = ((r * 0.272 + g * 0.534 + b * 0.131) as u8).min(255);
    }
}

/// 明るさ調整(factor: 0.0〜2.0、1.0で変化なし)
#[wasm_bindgen]
pub fn adjust_brightness(image_data: &mut [u8], factor: f32) {
    for i in (0..image_data.len()).step_by(4) {
        image_data[i] = ((image_data[i] as f32 * factor) as u8).min(255);
        image_data[i + 1] = ((image_data[i + 1] as f32 * factor) as u8).min(255);
        image_data[i + 2] = ((image_data[i + 2] as f32 * factor) as u8).min(255);
    }
}

/// コントラスト調整(factor: 0.0〜2.0、1.0で変化なし)
#[wasm_bindgen]
pub fn adjust_contrast(image_data: &mut [u8], factor: f32) {
    let intercept = 128.0 * (1.0 - factor);
    for i in (0..image_data.len()).step_by(4) {
        image_data[i] = ((image_data[i] as f32 * factor + intercept).clamp(0.0, 255.0)) as u8;
        image_data[i + 1] = ((image_data[i + 1] as f32 * factor + intercept).clamp(0.0, 255.0)) as u8;
        image_data[i + 2] = ((image_data[i + 2] as f32 * factor + intercept).clamp(0.0, 255.0)) as u8;
    }
}

/// 色反転(ネガフィルム効果)
#[wasm_bindgen]
pub fn invert_colors(image_data: &mut [u8]) {
    for i in (0..image_data.len()).step_by(4) {
        image_data[i] = 255 - image_data[i];
        image_data[i + 1] = 255 - image_data[i + 1];
        image_data[i + 2] = 255 - image_data[i + 2];
    }
}

/// Sobel エッジ検出
/// width・height を受け取ってエッジを検出する
#[wasm_bindgen]
pub fn apply_edge_detection(image_data: &[u8], width: u32, height: u32) -> Vec {
    let w = width as usize;
    let h = height as usize;
    let mut output = vec![0u8; image_data.len()];

    // まずグレースケール変換
    let mut gray = vec![0u8; w * h];
    for y in 0..h {
        for x in 0..w {
            let idx = (y * w + x) * 4;
            let r = image_data[idx] as f32;
            let g = image_data[idx + 1] as f32;
            let b = image_data[idx + 2] as f32;
            gray[y * w + x] = (r * 0.299 + g * 0.587 + b * 0.114) as u8;
        }
    }

    // Sobelフィルター(端のピクセルは除外)
    for y in 1..h-1 {
        for x in 1..w-1 {
            let gx = -(gray[(y-1)*w + (x-1)] as i32)
                + (gray[(y-1)*w + (x+1)] as i32)
                - 2*(gray[y*w + (x-1)] as i32)
                + 2*(gray[y*w + (x+1)] as i32)
                - (gray[(y+1)*w + (x-1)] as i32)
                + (gray[(y+1)*w + (x+1)] as i32);

            let gy = -(gray[(y-1)*w + (x-1)] as i32)
                - 2*(gray[(y-1)*w + x] as i32)
                - (gray[(y-1)*w + (x+1)] as i32)
                + (gray[(y+1)*w + (x-1)] as i32)
                + 2*(gray[(y+1)*w + x] as i32)
                + (gray[(y+1)*w + (x+1)] as i32);

            let magnitude = ((gx*gx + gy*gy) as f32).sqrt().min(255.0) as u8;
            let idx = (y * w + x) * 4;
            output[idx] = magnitude;
            output[idx + 1] = magnitude;
            output[idx + 2] = magnitude;
            output[idx + 3] = 255;
        }
    }

    output
}

/// ピクセル数を返す(テスト用)
#[wasm_bindgen]
pub fn pixel_count(image_data: &[u8]) -> u32 {
    (image_data.len() / 4) as u32
}

ビルドとWebページへの組み込み

# WebAssembly としてビルドする
wasm-pack build --target web

# 開発時は --dev オプションでデバッグ情報を含める
wasm-pack build --target web --dev

# 生成されるファイル
pkg/
├── wasm_image_processor.js    # JavaScript バインディング(wasm-bindgenが自動生成)
├── wasm_image_processor_bg.wasm  # バイナリ(Rustのコードがコンパイルされたもの)
├── wasm_image_processor.d.ts  # TypeScript型定義ファイル(自動生成)
└── package.json               # npm公開用(ローカル利用時は不要)

index.html:Wasmを読み込んで使う完全版

<!-- index.html - Wasm を読み込んで使う -->
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Wasm 画像フィルター</title>
  <style>
    body { font-family: sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; }
    canvas { max-width: 100%; border: 1px solid #ddd; border-radius: 8px; }
    .controls { display: flex; gap: 8px; flex-wrap: wrap; margin: 12px 0; }
    button { padding: 8px 16px; border: none; background: #3b82f6; color: white;
             border-radius: 6px; cursor: pointer; }
    button:hover { background: #2563eb; }
    .timing { font-size: 12px; color: #666; }
  </style>
</head>
<body>
  <h1>WebAssembly 画像フィルター</h1>
  <input type="file" id="fileInput" accept="image/*">

  <div class="controls">
    <button onclick="applyFilter('grayscale')">グレースケール</button>
    <button onclick="applyFilter('sepia')">セピア</button>
    <button onclick="applyFilter('invert')">色反転</button>
    <button onclick="applyFilter('edge')">エッジ検出</button>
    <button onclick="applyFilter('brightness')">明るさ +20%</button>
    <button onclick="applyFilter('contrast')">コントラスト +30%</button>
    <button onclick="resetImage()">リセット</button>
  </div>

  <p class="timing" id="timing">フィルターを適用するとここに処理時間が表示されます</p>

  <canvas id="canvas"></canvas>

  <script type="module">
    import init, {
      apply_grayscale, apply_sepia, apply_edge_detection,
      invert_colors, adjust_brightness, adjust_contrast
    } from './pkg/wasm_image_processor.js'

    // Wasm モジュールを初期化
    await init()

    const canvas = document.getElementById('canvas')
    const ctx = canvas.getContext('2d')
    let originalImageData = null

    // 画像ファイルの読み込み
    document.getElementById('fileInput').addEventListener('change', (e) => {
      const file = e.target.files[0]
      const img = new Image()
      img.src = URL.createObjectURL(file)
      img.onload = () => {
        canvas.width = img.width
        canvas.height = img.height
        ctx.drawImage(img, 0, 0)
        originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
      }
    })

    window.resetImage = function() {
      if (originalImageData) ctx.putImageData(originalImageData, 0, 0)
    }

    window.applyFilter = function(filter) {
      if (!originalImageData) return

      // 元の画像データのコピーを取得
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
      const start = performance.now()

      switch (filter) {
        case 'grayscale':
          apply_grayscale(imageData.data)
          ctx.putImageData(imageData, 0, 0)
          break
        case 'sepia':
          apply_sepia(imageData.data)
          ctx.putImageData(imageData, 0, 0)
          break
        case 'invert':
          invert_colors(imageData.data)
          ctx.putImageData(imageData, 0, 0)
          break
        case 'brightness':
          adjust_brightness(imageData.data, 1.2)
          ctx.putImageData(imageData, 0, 0)
          break
        case 'contrast':
          adjust_contrast(imageData.data, 1.3)
          ctx.putImageData(imageData, 0, 0)
          break
        case 'edge': {
          // エッジ検出は新しい配列を返す
          const result = apply_edge_detection(
            imageData.data, canvas.width, canvas.height
          )
          const resultImageData = new ImageData(
            new Uint8ClampedArray(result), canvas.width, canvas.height
          )
          ctx.putImageData(resultImageData, 0, 0)
          break
        }
      }

      const elapsed = (performance.now() - start).toFixed(2)
      document.getElementById('timing').textContent =
        `処理時間: ${elapsed}ms(Wasm)`
    }
  </script>
</body>
</html>

Vite + React への統合方法

# Vite + React プロジェクトにWasmを組み込む
npm create vite@latest my-wasm-app --template react-ts
cd my-wasm-app

# Wasm対応プラグインをインストール
npm install vite-plugin-wasm vite-plugin-top-level-await
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'

export default defineConfig({
  plugins: [
    react(),
    wasm(),
    topLevelAwait(), // Wasmの初期化にtop-level awaitを使うため
  ],
})
// src/hooks/useImageProcessor.ts
import { useState, useEffect, useRef, useCallback } from 'react'

type FilterType = 'grayscale' | 'sepia' | 'invert' | 'edge' | 'brightness' | 'contrast'

export function useImageProcessor() {
  const [wasmReady, setWasmReady] = useState(false)
  const wasmRef = useRef<{
    apply_grayscale: (data: Uint8ClampedArray) => void
    apply_sepia: (data: Uint8ClampedArray) => void
    apply_edge_detection: (data: Uint8ClampedArray, w: number, h: number) => Uint8Array
    invert_colors: (data: Uint8ClampedArray) => void
    adjust_brightness: (data: Uint8ClampedArray, f: number) => void
    adjust_contrast: (data: Uint8ClampedArray, f: number) => void
  } | null>(null)

  useEffect(() => {
    async function loadWasm() {
      const wasm = await import('../../pkg/wasm_image_processor.js')
      await wasm.default()  // Wasmモジュールを初期化
      wasmRef.current = wasm
      setWasmReady(true)
    }
    loadWasm()
  }, [])

  const applyFilter = useCallback((
    canvas: HTMLCanvasElement,
    filter: FilterType
  ): number => {
    if (!wasmRef.current) return 0

    const ctx = canvas.getContext('2d')!
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
    const wasm = wasmRef.current
    const start = performance.now()

    switch (filter) {
      case 'grayscale': wasm.apply_grayscale(imageData.data); break
      case 'sepia': wasm.apply_sepia(imageData.data); break
      case 'invert': wasm.invert_colors(imageData.data); break
      case 'brightness': wasm.adjust_brightness(imageData.data, 1.2); break
      case 'contrast': wasm.adjust_contrast(imageData.data, 1.3); break
      case 'edge': {
        const result = wasm.apply_edge_detection(imageData.data, canvas.width, canvas.height)
        const resultImageData = new ImageData(
          new Uint8ClampedArray(result), canvas.width, canvas.height
        )
        ctx.putImageData(resultImageData, 0, 0)
        return performance.now() - start
      }
    }

    ctx.putImageData(imageData, 0, 0)
    return performance.now() - start
  }, [])

  return { wasmReady, applyFilter }
}

Web Workers + Wasm の組み合わせ

WebAssemblyは高速ですが、メインスレッドで実行するとUIをブロックする可能性があります(特に4K以上の大きな画像)。Web WorkerとWebAssemblyを組み合わせることで、UIのフリーズを防ぎながら高速処理を実現できます。

// wasm-worker.js - Wasm を Worker 内で実行する

import init, { apply_grayscale, apply_sepia } from './pkg/wasm_image_processor.js'

// Worker起動時にWasmを初期化
await init()

self.addEventListener('message', async (event) => {
  const { buffer, filter, width, height } = event.data
  const imageData = new Uint8ClampedArray(buffer)

  const start = performance.now()

  switch (filter) {
    case 'grayscale':
      apply_grayscale(imageData)
      break
    case 'sepia':
      apply_sepia(imageData)
      break
  }

  const elapsed = performance.now() - start

  // 処理済みバッファをTransferableで返す(コピーなし)
  self.postMessage(
    { buffer: imageData.buffer, elapsed },
    [imageData.buffer]  // 所有権を移転して返す
  )
})
// main.ts - Worker経由でWasm処理を行う

const wasmWorker = new Worker(new URL('./wasm-worker.js', import.meta.url), {
  type: 'module'
})

async function applyFilterInWorker(canvas: HTMLCanvasElement, filter: string) {
  const ctx = canvas.getContext('2d')!
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
  const buffer = imageData.data.buffer

  return new Promise<number>((resolve) => {
    wasmWorker.addEventListener('message', (e) => {
      const resultData = new Uint8ClampedArray(e.data.buffer)
      const resultImageData = new ImageData(resultData, canvas.width, canvas.height)
      ctx.putImageData(resultImageData, 0, 0)
      resolve(e.data.elapsed)
    }, { once: true })

    // TransferableでWorkerに送る
    wasmWorker.postMessage(
      { buffer, filter, width: canvas.width, height: canvas.height },
      [buffer]
    )
  })
}

既存のWasmライブラリを活用する

自分でRustを書かなくても、既にWebAssemblyに対応した既存ライブラリを活用する方法があります。

ライブラリ用途特徴
ffmpeg.wasm動画エンコード・変換・サムネイル生成C/C++のffmpegをWasm化。ブラウザで動画変換が可能
OpenCV.js画像認識・顔検出・特徴点抽出OpenCVのWasm版。コンピュータビジョン全般に対応
Sharp(wasm版)高速画像リサイズ・変換Node.jsの画像ライブラリをブラウザで使える
SQLite(sql.js)ブラウザ上でSQLiteサーバーなしでSQL実行・データ分析
TensorFlow Lite for Web機械学習推論軽量モデルをWasmで高速推論
// ffmpeg.wasm の使用例:ブラウザで動画をGIFに変換

import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'

const ffmpeg = createFFmpeg({ log: true })

async function convertToGif(videoFile: File): Promise<string> {
  await ffmpeg.load()

  const inputName = 'input.mp4'
  const outputName = 'output.gif'

  ffmpeg.FS('writeFile', inputName, await fetchFile(videoFile))

  // FFmpegコマンドで変換(10fps、480px幅のGIF)
  await ffmpeg.run(
    '-i', inputName,
    '-vf', 'fps=10,scale=480:-1:flags=lanczos',
    '-loop', '0',
    outputName
  )

  const data = ffmpeg.FS('readFile', outputName)
  const url = URL.createObjectURL(new Blob([data.buffer], { type: 'image/gif' }))

  return url
}

メモリ管理とトラブルシューティング

WebAssemblyは線形メモリを使います。デフォルトでは初期64KBから最大4GBまで拡張可能です。Rustのwasm-bindgenを使う場合は自動的にメモリ管理が行われるため、通常は意識する必要はありません。

// Wasmのメモリ使用量を監視する

// Wasm インスタンスのメモリ情報を確認
import init, { __wasm } from './pkg/wasm_image_processor.js'
await init()

// WebAssembly.Memory オブジェクトから使用量を確認
const memory = __wasm.memory
console.log(`Wasmメモリ: ${memory.buffer.byteLength / 1024 / 1024}MB`)

// 処理後のメモリ解放(Rustのwasm-bindgenが管理するため通常不要)
// ただし Vec<u8> を返す関数は呼び出し後に自動解放される

よくあるエラーと解決方法

エラー原因解決方法
WebAssembly.instantiate(): ...is not an ArrayBufferwasmファイルのMIMEタイプが間違っているサーバーでapplication/wasmを設定
TypeError: wasm.xxx is not a functionwasm-bindgenのバインディングが未生成wasm-pack buildを再実行
Wasmが初期化前に呼ばれたawait init()の前に関数を呼んでいる必ずawait init()の後に使用する
メモリ不足エラー大きすぎる画像を一度に処理画像を分割処理するか、Web Workerに移す

WebAssemblyの実用的なユースケース5選

【WebAssembly が特に効果的なユースケース】

1. 画像・動画処理
→ OpenCV.js(画像認識・顔検出)のWasm版
→ ffmpeg.wasm でブラウザ上での動画エンコード・変換
→ リアルタイム動画フィルター(ストリーミング処理)

2. ゲーム開発
→ UnityのWebGLビルド(WebAssembly + WebGL)
→ Unreal EngineのHTML5ビルド
→ 物理エンジン(Bullet Physics のWasm版)

3. 暗号化・セキュリティ
→ Rustで書いた暗号ライブラリをクライアントサイドで高速実行
→ 秘密鍵をサーバーに送らずブラウザ内で処理(e2e暗号化)
→ PasswordのKDFをWasmで高速化

4. データ処理・分析
→ 大量CSVのパース・集計をWasmで高速化
→ SQLiteのWasm版(sqlite3.wasm)でブラウザ上でSQL実行
→ DuckDB-WASM によるブラウザ上のOLAP分析

5. 科学計算・シミュレーション
→ 物理シミュレーション(流体・粒子系・分子動力学)
→ 機械学習推論(TensorFlow Lite for WebAssembly)
→ 音声処理・音楽合成エンジン

よくある質問

RustでないとWebAssemblyは書けませんか?

いいえ。C/C++(Emscripten)・Go(GOARCH=wasm)・AssemblyScript(TypeScriptライク)・Zigなどでも書けます。RustはメモリセーフでWebAssemblyとのエコシステム(wasm-bindgen・wasm-pack)が充実しているため、新規開発では最もおすすめです。JavaScriptに近い文法でWasmを書きたい場合はAssemblyScriptが入門しやすいです。

WebAssemblyはすべての処理を置き換えられますか?

計算集約型の処理にのみ有効です。DOM操作・イベント処理・API呼び出しはJavaScriptが担います。WasmとJavaScript間のデータ転送にはオーバーヘッドがあるため、小さなデータの頻繁なやりとりよりも、大きなデータを一括処理するケースに向いています。

WebAssemblyのバンドルサイズはどのくらいですか?

Rustで書いた簡単な画像処理ライブラリだと、最適化後(opt-level=3lto=true)のwasmバイナリは10〜50KB程度になります。gzip圧縮後はさらに小さくなります。ffmpeg.wasmのような大型ライブラリは10MB以上になるため、遅延読み込み(動的importまたはCDN)が推奨されます。

Wasmのデバッグ方法を教えてください

wasm-pack build --devでデバッグ情報を含めてビルドすると、Chrome DevToolsでRustのソースコードにステップインできます(DWARF debuginfoが有効な場合)。また#[wasm_bindgen]付きの関数にweb_sys::console::log_1()を挿入してJavaScriptのコンソールにログを出す方法も有効です。

WasmをNPMパッケージとして公開できますか?

できます。wasm-pack publishコマンドで生成されたpkg/ディレクトリをnpmに公開できます。--target bundlerオプションでVite/Webpack向けに最適化されたパッケージを生成できます。


まとめ

  • WebAssemblyはJavaScriptより2〜4倍高速:計算集約型の画像処理・暗号化・大量データ処理に特に有効。DOM操作はJavaScriptが担う役割分担が基本
  • 開発フロー:Rustで処理を書く → wasm-pack build --target web → 生成された.js/.wasmファイルをHTMLやViteから読み込む
  • JavaScript連携パターン:CanvasのImageDataを渡してWasmで処理し結果を返す方法が画像処理の基本。大きなデータはTransferableで渡すと効率的
  • Web Workers組み合わせ:WasmをWorker内で実行することで、UIのフリーズを防ぎながら高速処理できる
  • 既存ライブラリ活用:ffmpeg.wasm・OpenCV.js・SQLite.wasmなど豊富なWasmライブラリを活用すれば、Rustを書かずに高速処理を組み込める
  • メモリ管理:Rustのwasm-bindgenが自動管理するため通常は不要。大きな画像は分割処理かWeb Workerへの移行で対処する

WebAssemblyは「JavaScriptの限界」を感じた場面で選ぶ技術です。既存のRust/C++ライブラリを活用できる点も大きな強みです。Web WorkersとWasmの組み合わせで、UIのフリーズなく高速処理を実現できます。まず小さなユースケースで試してみましょう。


WithCodeを体験できる初級コース公開中!

この記事を書いた人

WithCode(ウィズコード)は「目指すなら稼げる人材」をビジョンに、累計400名以上のフリーランスを輩出してきた超実践型プログラミングスクールです。150社以上の実案件支援を特徴にWeb制作・Webデザインなどの役立つ情報を現場のノウハウに基づいて発信していきます。

– service –WithGroupの運営サービス

  • WithCode
    - ウィズコード -

    スクール

    「未経験」から
    現場で通用する
    スキルを身に付けよう!

    詳細はこちら
  • WithFree
    - ウィズフリ -

    実案件サポート

    制作会社のサポート下で
    実務経験を積んでいこう!

    詳細はこちら
  • WithCareer
    - ウィズキャリ -

    就転職サポート

    大手エージェントのサポート下で
    キャリアアップを目指そう!

    詳細はこちら

公式サイト より
今すぐ
無料カウンセリング
予約!

目次