Upgrade to Pro — share decks privately, control downloads, hide ads and more …

.NET WebAssemblyで実現するクライアントサイドAI推論:NuGetからViteま...

Avatar for Yusuke Yamada Yusuke Yamada
September 19, 2026

.NET WebAssemblyで実現するクライアントサイドAI推論:NuGetからViteまで、2つのエコシステムを繋ぐビルド戦略

C# Kaigi 2026 ( https://csharpkaigi.net/ , https://csharpkaigi.connpass.com/event/394163/ ) でお話したセッションスライドです。
VoicevoxEngineSharpを.NET WebAssemblyでwasm化し、ブラウザ上でVOICEVOXが動作するようになるまでの事例を元に、.NET WebAssemblyとWebとの共存について話した内容です。

関連URL
- https://github.com/yamachu/voicevox/tree/web-sw
- https://zenn.dev/yamachu/articles/e6fc24d8104e9e
- https://zenn.dev/yamachu/articles/d708a941e4590e
- https://zenn.dev/yamachu/articles/1e6f15baa2eea8
- https://zenn.dev/j_sakamoto/articles/8bceb2d025f0db

Avatar for Yusuke Yamada

Yusuke Yamada

September 19, 2026

More Decks by Yusuke Yamada

Other Decks in Technology

Transcript

  1. C# Kaigi 2026 / Park // .NET WebAssemblyで実現する クライアントサイドAI推論 C#

    Kaigi 2026 NuGetからViteまで、 2つのエコシステムを繋ぐビルド戦略 main • UTF-8 • Markdown
  2. C# Kaigi 2026 / Park EXPLORER › profiles › yamachu.md

    yamachu インターネットでは ちゅうこ / 合瀬奏 の名前でも活動 HELLO, I'M Web Frontend Engineer Microsoft MVP VTuber VS Code / TypeScript / Web Frontend C# は趣味で書いています yamachu X @y_chu5 YouTube @awasesou GitHub main • UTF-8 • Markdown 2 / 24
  3. C# Kaigi 2026 / Park EXPLORER › demo › voicevox-on-browser.md

    LIVE DEMO ▶ ブラウザ上で動作するVOICEVOX 音声合成の処理まで、 ブラウザの中へ main • LIVE DEMO 3 / 24
  4. C# Kaigi 2026 / Park › intro › ecosystems.md EXPLORER

    // ブラウザの中に、 2つのエコシステムがある Web UI Web Frontend ユーザー体験 既存の画面 .NET WebAssembly C# JS interop Native dependency UIをBlazorへ移行する話ではない 既存Web UIと.NET WebAssemblyを共存させる main • UTF-8 • Markdown 4 / 24
  5. C# Kaigi 2026 / Park EXPLORER › intro › takeaways.md

    // この25分で 持ち帰ってほしいこと 選択肢を知る C#資産をブラウザへ 持ち込む、という選択肢 難所を予測する API / Native / Build の どこで詰まるか 自分で判断する 価値とコストを見て、 使いどころを決める main • UTF-8 • Markdown 5 / 24
  6. C# Kaigi 2026 / Park EXPLORER › map › boundaries.md

    // 2つのエコシステムを繋ぐ、 3つの境界 Web UI API .NET WebAssembly Vite Build .NET build Native Native dependency main • UTF-8 • Markdown 6 / 24
  7. C# Kaigi 2026 / Park EXPLORER › intro › browser-actors.md

    // ブラウザ側の登場人物 Web UI 既存の画面 JS / TSの世界 Service Worker に割り込む Proxy / Middleware的な 入口 fetch Web Worker UIから分離した実行場所 postMessage で仕事を渡す .NET WebAssembly C#ライブラリを ブラウザ内の計算エンジン へ main • UTF-8 • Markdown 7 / 24
  8. C# Kaigi 2026 / Park EXPLORER › boundaries › api.md

    // API Boundary 1 / 3 既存Web UIとの 「契約」をどう守る? main • UTF-8 • Markdown 8 / 24
  9. C# Kaigi 2026 / Park EXPLORER › api › existing-http.md

    API Native Build // 既存Web UIは、 HTTPを喋っている fetch("/audio_query") fetch("/synthesis") VOICEVOX UI ENGINEをブラウザへ移しても、 Web UIまで書き換えたくない VOICEVOX ENGINE HTTP API main • UTF-8 • Markdown 9 / 24
  10. C# Kaigi 2026 / Park EXPLORER › api › service-worker.md

    API Native Build // Service Workerで、HTTPの契約を残す Web UI fetch("/synthesis") Service Worker HTTP Adapter delegate .NET WebAssembly 契約を変えず、実装の置き場所だけ変える main • UTF-8 • Markdown 10 / 24
  11. C# Kaigi 2026 / Park › api › worker-split.md EXPLORER

    API Native Build // HTTPを受ける場所と推論する場所を分ける Web UI HTTP Service Worker HTTP Adapter Message Web Worker .NET WebAssembly AI inference API Boundary:Web UIの契約を守る main • UTF-8 • Markdown 11 / 24
  12. C# Kaigi 2026 / Park EXPLORER › api › csharp-hub.md

    API Native Build // C#への入口も、C#からの出口もある using System.Runtime.InteropServices.JavaScript; public static partial class EngineBridge { Web側に強いものはWeb側で、 Native資産はNativeのまま使う [JSExport] internal static byte[] Synthesis(...); [JSImport("runInference", "./inference.js")] internal static partial Task<float[]> RunInference(...); } Web Worker ↓ [JSExport] C# ├─ [JSImport] → JS wrapper │ └─ onnxruntime-web └─ P/Invoke → Native dependency main • UTF-8 • Markdown 12 / 24
  13. C# Kaigi 2026 / Park EXPLORER › boundaries › native.md

    // Native Boundary 2 / 3 C#の下にある Native dependencyをどう持ち込む? main • UTF-8 • Markdown 13 / 24
  14. C# Kaigi 2026 / Park EXPLORER › native › nativefile-reference.md

    ✓ API Native Build // C#だけでは、完結していない 例:OpenJTalkのような、C#の下にいるNative dependency コードから⾒ると ビルドでは C# Native dependency (.a / .o / .wasm) P/Invoke Native API NativeFileReference .NET WebAssembly build Emscripten static link Final WebAssembly main • UTF-8 • Markdown 14 / 24
  15. C# Kaigi 2026 / Park › native › emscripten.md EXPLORER

    ✓ API Native Build // .NETのターゲットが変わると、使われる Emscriptenも変わる TargetFramework net8.0 net9.0 / 10.0 net11.0 Emscripten 3.1.34 3.1.56 6.0.3 ズレると? wasm-ld: error: undefined symbol TargetFrameworkを変えると、 Native側も再ビルドが必要になる可能性がある main • UTF-8 • Markdown 15 / 24
  16. C# Kaigi 2026 / Park EXPLORER › native › nuget.md

    ✓ API Native Build // NuGetでtoolchain差を吸収する runtimes/browser-wasm/ ├─ 3.1.34/ │ └─ openjtalk.a ├─ 3.1.56/ │ └─ openjtalk.a └─ 6.0.3/ └─ openjtalk.a <NativeFileReference Include=".../$(EmscriptenVersion)/openjtalk.a" /> Native Boundary:toolchain差を パッケージ利用者へ漏らさない main • UTF-8 • Markdown 16 / 24
  17. C# Kaigi 2026 / Park EXPLORER › boundaries › build.md

    // Build Boundary 3 / 3 「動く」を 「普段使える」にする main • UTF-8 • Markdown 17 / 24
  18. C# Kaigi 2026 / Park EXPLORER › build › wiring.md

    ✓ API ✓ Native Build // 動くが、Viteは.NETの成果物を知らない dotnet build C# code .NET Wasm output _framework/ 変更追従 成果物の配布 dotnet build dotnet.js を どう追従させる? ??? の URL / base pathは? Vite dev / build 本番build Vite成果物へ どう含める? main • UTF-8 • Markdown 18 / 24
  19. C# Kaigi 2026 / Park › build › vite-plugin.md EXPLORER

    ✓ API ✓ Native Build // Web側のビルドの入口を変えない import dotnetWasm from '@yamachu/vite-plugin-dotnet-wasm'; dotnetWasm({ projectPath: "./Engine.csproj", watch: true, }) pnpm dev Vite dotnet build / watch output Browser Build Boundary: pnpm dev の裏で dotnet build を動かす main • UTF-8 • Markdown 19 / 24
  20. C# Kaigi 2026 / Park EXPLORER › recap › connected.md

    // 3つの境界を、 こう繋いだ VOICEVOX UI API Service Worker Message Web Worker .NET WebAssembly Native Vite Build vite-plugindotnet-wasm dotnet build Native dependency main • UTF-8 • Markdown 20 / 24
  21. C# Kaigi 2026 / Park EXPLORER › recap › principle.md

    // 境界を作る目的は、 相手側のエコシステムを意識させないこと API HTTPのまま Native P/Invokeのまま Build pnpm dev のまま main • UTF-8 • Markdown 21 / 24
  22. C# Kaigi 2026 / Park EXPLORER › decision › when.md

    // 採用するなら、どんな条件? 価値が大きく、コストを許容できるとき 持ち込む価値が大きい 既存C#資産を再利用したい Native dependencyごと使いた い サーバーへ送らずローカル実行し たい 追加コストを許容できる 初回ロード量 .NET / EmscriptenのToolchain 管理 Webビルド統合の維持 main • UTF-8 • Markdown 22 / 24
  23. C# Kaigi 2026 / Park EXPLORER › resources › try.md

    // この順番で試してみませんか? 小さい構成から、実アプリへ 01 02 03 Vite + .NET WebAssembly @yamachu/vite-plugindotnet-wasm NativeFileReference repo: yamachu/voicevox web-sw branch まず動かす Nativeを足す 実アプリを見る main • UTF-8 • Markdown 23 / 24
  24. C# Kaigi 2026 / Park EXPLORER › closing › summary.md

    // 必要なところだけ、 C#を連れてくる SUMMARY Web ⇄ .NET 境界で「違い」を吸収する API / Native / Build どちらか一方に寄せなくてもいい main • UTF-8 • Markdown 24 / 24