vgpu(vercel-labs/vgpu・GitHubスター1,372・MIT)は、WebGPUを扱うためのモジュール式ライブラリです。シェーダー、3Dシーン、GPUテンソル、ニューラルネットワーク、数式の可視化——と守備範囲は広く、Vercel Labsが開発しています。
この手のライブラリで気になるのは「謳っているとおりに動くのか」です。とくにvgpuは「同じコードがブラウザでもNodeでもモックでも動く」というcross-runtimeを売りにしています。そこでnpmから実際に入れて、3つの経路すべてを走らせました。結論から言うと、APIの形は本当に共通で、一方でモックとNodeにはそれぞれ知っておくべき性質がありました。
30秒でわかる vgpu
・何ができるか:WebGPUのパイプライン(シェーダー・描画パス・計算パス)を短い関数で組み立てる。.wgsl を型付きモジュールとして扱える
・何を解決するか:生のWebGPU APIの冗長さと、テスト時にGPUが要る問題
・実測①:3エントリポイントの共通エクスポートは22個。違いはアダプタ生成関数だけで、設計どおり差し替え可能だった
・実測②:vgpu/mock が返すのはピクセルではなくAPI呼び出しの記録。vgpu/node(Dawn)は動く環境が限られる
この記事のポイント
・モックで描画して読み出すと262,144バイト返るが中身は全てゼロ。ラスタライズしないので当然で、代わりに呼び出し回数とディスクリプタを検証する
・Node経路は手元のmacOS 14 / arm64で失敗。エラーは vgpu prebuilds currently target **Linux arm64 only** と明示していた
・npm i は約8秒だが node_modules は95MBになる(Dawnのバイナリを含むため)
フロントエンドの見た目まわりをどう組み立てるかという土台の整理はデザインシステムとは?仕組み・構成要素・有名事例をエンジニア向けに整理する【2026年版】にまとめてあります。vgpuはその上で「描画そのもの」を担う、もう一段低い層にあたります。
vgpuとは——WebGPUを短く書くための層
WebGPUを生で書くと、デバイス取得・パイプライン記述子・バインドグループ・コマンドエンコーダ……と定型のコードが長く続きます。vgpuはそこを短くします。READMEのNode向けクイックスタートはこうです。
import { draw, frame, init, target } from "vgpu/node";
import triangleShader from "./triangle.wgsl";
const gpu = await init();
const colorTarget = target(gpu, { size: [256, 256], format: "rgba8unorm" });
const triangle = draw(gpu, { shader: triangleShader });
frame(gpu, (f) => f.pass(colorTarget, triangle));
const pixels = await colorTarget.read();
gpu.dispose();
init でデバイスを取り、target で描画先、draw で描画物、frame で1フレーム分の記録——という流れです。生のWebGPUに比べると明らかに短くなります。
UIの部品を組み合わせて画面を作る話とは層が違う点に注意してください。square-ui徹底解説|shadcn/uiのレイアウト集のような部品集が「何を並べるか」を扱うのに対し、vgpuが扱うのは「その1枚をGPUでどう描くか」です。併用はできますが、解決している問題が重なりません。
リポジトリはpnpmのモノレポで、8つのパッケージに分かれています。
| パッケージ | 役割 |
|---|---|
core |
中核。デバイス抽象とモックGPU |
render |
描画まわり |
wgsl / wgsl-std |
WGSLのモジュール解決と標準ライブラリ |
adapter-node |
Dawn経由のNode実行 |
adapter-mock |
GPU不要のモック |
vgpu / vgpu-api |
公開パッケージとAPI定義 |
npmに出ているのは vgpu で、実測時点のバージョンは v0.3.1、ライセンスは MIT でした。
実測:導入とエントリポイントの突き合わせ
まず入れます。以下のコマンドは実際に実行しています。
mkdir vg && cd vg && npm init -y
npm pkg set type=module
npm i [email protected]
約8秒で完了しました(npmキャッシュが効いた2回目は約2秒)。ただし node_modules は 95MB になります。Dawnのネイティブバイナリを含むためで、ブラウザ向けにしか使わない場合でもこのサイズがインストールされます。
次に「同じAPI」という主張を確かめます。エクスポートを機械的に突き合わせました。
const [n, m, main] = await Promise.all([
import('vgpu/node'), import('vgpu/mock'), import('vgpu')
]);
const N = new Set(Object.keys(n)), M = new Set(Object.keys(m));
const only = (a, b) => [...a].filter(x => !b.has(x)).sort();
console.log('node のみ:', only(N, M).join(' '));
console.log('mock のみ:', only(M, N).join(' '));
console.log('共通数 :', [...N].filter(x => M.has(x)).length);
結果はこうでした。
| 比較 | 結果 |
|---|---|
| 共通エクスポート数 | 22 |
vgpu/node のみ |
createNodeAdapter |
vgpu/mock のみ |
createMockAdapter / getMockGPUDeviceInstrumentation |
既定 vgpu のみ |
なし |
差分はアダプタ生成関数だけでした。init target draw frame compute storage uniforms といった実際に書く側の関数は3経路とも同名で揃っています。「経路を差し替えても同じコードが動く」という設計は、少なくともAPIの形としては本物です。
実測:vgpu/mock が返すのはピクセルではない
ここが一番の注意点です。READMEは「vgpu/mock は決定的なソフトウェアアダプタに差し替えるので、テストにGPUが要らない」と書いています。これを読むと「GPUなしでレンダリング結果を検証できる」と受け取りがちですが、実際は違いました。
三角形を描くWGSLを用意し、256×256で描画して読み出しました。
# 記事の検証で使った最小構成(.wgsl は readFileSync で文字列として読む)
node run.mjs mock
# [mock] init=54ms render+read=7ms bytes=262144 不透明px=0
# [mock] 中央画素 rgba=0,0,0,0
読み出しは成功します。262,144バイト(256×256×4)がきちんと返ります。しかし中身は全てゼロでした。アルファが0でない画素は1つもありません。
理由は単純で、モックはラスタライズしないからです。ではモックで何を検証するのか——getMockGPUDeviceInstrumentation() を呼ぶと分かります。
calls = {"createBuffer":0,"createBindGroupLayout":0,"createBindGroup":0,
"createCommandEncoder":1,"createRenderBundleEncoder":0,
"createShaderModule":1,"createRenderPipeline":1, ...}
createCommandEncoderDescriptors len=1
createRenderPipelineDescriptors len=1
createBufferDescriptors len=0
呼び出し回数と、渡したディスクリプタそのものが記録されています。 つまりモックが検証させたいのは「意図したとおりのパイプラインを、意図した回数だけ組み立てたか」です。バッファを余計に作っていないか、パイプラインを毎フレーム作り直していないか——といった構成の回帰テストに向いています。
「GPUなしでテストできる」を「描画結果を検証できる」と読まない:モックで返るピクセルは常にゼロです。見た目の回帰を見たいなら、実GPU(ブラウザまたはDawn)での実行が必要になります。モックが担当するのはAPI呼び出しの形であって、出力画像ではありません。ここを取り違えると、通っているはずのテストが何も見ていない状態になります。
init / target / draw / frame"] --> B{"どのアダプタを使うか"} B -->|"vgpu(既定)"| C["ブラウザのWebGPU
実際に描画される"] B -->|"vgpu/node"| D["Dawnネイティブ
環境の条件あり"] B -->|"vgpu/mock"| E["呼び出しを記録
ピクセルは常にゼロ"] C --> F["見た目の検証はここ"] D --> F E --> G["構成の回帰テストはここ"]
実測:Node(Dawn)経路は環境を選ぶ
同じコードを vgpu/node で走らせると、手元では失敗しました。macOS 14(Darwin 23.5.0)/ arm64 の環境です。
VGPUError: Stock webgpu failed to load on darwin/arm64.
No portable Dawn prebuild is available for darwin/arm64:
unsupported platform darwin/arm64;
vgpu prebuilds currently target Linux arm64 only.
原因は2段階でした。
・vgpu自身の事前ビルドは、エラーが明言しているとおり現状Linux arm64のみ
・代わりに試される [email protected] の同梱バイナリは macOS 15 向けにビルドされており、macOS 14では Symbol not found: _OBJC_CLASS_$_MTLLogStateDescriptor で読み込めない
つまり「macOSでNode経路を使うなら、少なくともmacOS 15以降が要る」ということになります。
エラーメッセージの出来はよい:この失敗、原因が2経路にまたがっているのに、メッセージが両方を1文で説明しています(vgpu側の事前ビルドが無いこと、フォールバック先も失敗したこと)。ライブラリの品質はこういうところに出ます。読者が同じ壁に当たったとき、メッセージだけで次の一手が決まるはずです。
未検証の範囲:ブラウザ経路(既定の vgpu)は本記事では実行していません。またLinux arm64でのDawn経路も試していないため、そこで期待どおり動くかは未確認です。確認したのは「モックが動くこと」「macOS 14 arm64ではNode経路が動かないこと」の2点です。
開発状況と、いま使うかの判断
2026年9月2日時点の実測です。
| 観測項目 | 実測値 |
|---|---|
| スター / フォーク | 1,372 / 66 |
| ライセンス | MIT(21行の標準文面) |
| 最新リリース | v0.3.1(2026-08-26) |
| 既定ブランチ最終push | 2026-09-01(前日) |
| オープンIssue | 46件 |
| リポジトリ作成 | 2026-05-05 |
開発は活発です。作成から4か月弱でv0.3.1まで来ており、最終pushは前日でした。この記事で扱った他のツールのように「star だけ伸びて開発が止まっている」パターンではありません。
一方で1.0前です。APIが変わる前提で採用するか、用途を絞るのが無難でしょう。
向いているケース
・WebGPUのシェーダーを書きたいが、定型コードを減らしたい
・パイプラインの構成をテストで固定したい。モックの呼び出し記録はこの用途に素直に効きます
・GPUテンソルや数式可視化など、描画以外の計算パスにも興味がある
注意が必要なケース
・Node経路を主戦場にしたい。現状プラットフォームの条件が厳しめです
・node_modules 95MBが負担になる構成
・APIの安定を前提にした長期プロジェクト(v0.3.1・Issue 46件)
フロントエンド側の構造をどう分けるかという観点はクリーンアーキテクチャをフロントエンドに適用するでも整理しています。vgpuのように「描画の低い層」を触るライブラリは、UIの状態管理から切り離しておくと後が楽になります。
参照ソース
・vercel-labs/vgpu — GitHub リポジトリ(README・LICENSE・packages/ 構成。本記事のパッケージ一覧とライセンスはここを参照)
・vgpu — npm(本記事で導入した v0.3.1 の配布元)
・vgpu 公式サイト(ドキュメントとサンプル)