Pyodide:WebAssemblyでブラウザ上で本物のPythonを実行
Pyodideは CPython を WebAssembly にコンパイルし、NumPyやpandasをブラウザ上で動かします。仕組みと向いている用途を解説します。

ブラウザ上でPythonを動かすことは、2010年代初頭からずっと夢でした。ただ、どの試みも同じ壁にぶつかっています。Pythonは CPython の C ランタイムに深く依存しているため、Webに移植するのが簡単ではないのです。Transcrypt、Brython、Skulpt はいずれも Python を JavaScript で再実装することで解決しようとしました。単純なスクリプトなら動きましたが、NumPy や pandas、そしてPythonを実用的なものにしているC拡張ライブラリが必要になった途端に破綻しました。
Pyodide は別のアプローチを取ります。本物の CPython インタープリタを WebAssembly にコンパイルするのです。再実装ではなく、CPython のソースコードそのものを Emscripten でコンパイルし、ブラウザ上で動かします。そのため、C拡張を含めて言語の互換性が保たれます。import numpy と書けば動きます。コンパイル済みの Wasm バイナリに、実際の NumPy の C コードが含まれているからです。
Pyodide の内部動作
Pyodide のビルドでは、CPython のソースツリーを Emscripten でコンパイルします。Emscripten はネイティブの機械語ではなく WebAssembly をターゲットにするコンパイラツールチェーンです。その結果、Python インタープリタを実装した .wasm バイナリと、それをブラウザ環境につなぐ JavaScript のグルーコードができあがります。
厄介なのはC拡張です。たとえば NumPy には10万行を超える C と Fortran のコードが含まれています。Pyodide では NumPy、pandas、scikit-learn、matplotlib、scipy など、人気のあるパッケージを厳選して事前コンパイルし、Wasm モジュールとして必要な時に読み込めるようにしています。import numpy を実行すると、Pyodide は事前コンパイル済みの NumPy の Wasm モジュールをダウンロードし、実行中のインタープリタにリンクして初期化します。
<!-- Minimal Pyodide example -->
<script src="https://cdn.jsdelivr.net/pyodide/v0.27.0/full/pyodide.js"></script>
<script>
async function main() {
// Load the Python interpreter (~10MB download)
const pyodide = await loadPyodide();
// Run Python code directly
pyodide.runPython(`
import sys
print(f"Python {sys.version} running in the browser!")
`);
// Load packages on demand
await pyodide.loadPackage('numpy');
// Use NumPy — the real NumPy, compiled to Wasm
const result = pyodide.runPython(`
import numpy as np
arr = np.random.randn(1000)
f"Mean: {arr.mean():.4f}, Std: {arr.std():.4f}"
`);
console.log(result); // "Mean: 0.0123, Std: 1.0045"
}
main();
</script>
JavaScript と Python のブリッジ
Pyodide の最大の強みの一つは、Python と JavaScript のシームレスな相互運用です。Python のオブジェクトは自動的に JavaScript 側へプロキシされ、その逆も同様です。Python から JavaScript の関数を呼び出し、Python から DOM を操作し、手動でシリアライズすることなく2つの言語の間でデータを受け渡せます。
// JavaScript calling Python
const pyodide = await loadPyodide();
// Define a Python function
pyodide.runPython(`
def analyze(data):
import statistics
return {
'mean': statistics.mean(data),
'median': statistics.median(data),
'stdev': statistics.stdev(data)
}
`);
// Call it from JavaScript with JS data
const analyze = pyodide.globals.get('analyze');
const result = analyze([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
console.log(result.toJs()); // {mean: 5.5, median: 5.5, stdev: 3.03}
// Python accessing the DOM
pyodide.runPython(`
from js import document
element = document.getElementById('output')
element.textContent = 'Updated from Python!'
`);
プロキシの仕組みは型変換も自動で行います。Python の dict は JavaScript のオブジェクトに、list は配列に、数値は JavaScript の number になります。たとえば100万要素の NumPy 配列を JavaScript の可視化ライブラリに渡すような大量のデータ転送では、Pyodide は共有メモリバッファを使ってコピーを避けています。これはパフォーマンスの面で非常に重要です。
Pyodide が向いている用途
対話型の計算環境。 Pyodide 上に構築された JupyterLite は、Jupyter ノートブックをブラウザ内で完全に動かします。サーバーは不要で、Python カーネルはローカルの Wasm 上で動作します。教育の現場では大きな変化です。学生は何もインストールせずに Python ノートブックを動かせ、講師がサーバーを用意する必要もありません。
データ分析ツール。 ユーザーがデータをアップロードして分析を行う Web アプリでは、Pyodide を使ってデータ処理をすべてクライアント側で行えます。データがブラウザの外に出ることはないので、プライバシーの懸念を解消でき、サーバーコストもかかりません。pandas で変換処理を行う CSV アップローダーは、データをバックエンドに送るものより、デプロイが簡単でプライバシーも守れます。
ライブ例付きのドキュメント。 Python ライブラリのドキュメントに、実際に実行される対話型のコードブロックを載せられます。静的な出力を見せる代わりに、読者はコードを書き換えてすぐに結果を確認できます。学習効果は静的なサンプルよりずっと高くなります。
プロトタイピングと実験。 Python で考えるデータサイエンティストは、データ変換や可視化のプロトタイプをブラウザ上で直接作り、結果を URL として共有できます。環境構築も依存関係の管理も不要で、「自分の環境では動く」問題とも無縁です。
パフォーマンスの現実
Pyodide は速くありません。WebAssembly はネイティブコードと比べてオーバーヘッドがあり、計算負荷の高い処理では一般に1.5〜3倍遅くなります。さらに Pyodide は、Wasm にコンパイルされた CPython(純粋な Python のコードでは C よりすでに100倍ほど遅い)を動かしています。純粋な Python のループでは、ネイティブの CPython よりおおむね2〜5倍遅くなります。
ただ、Pyodide が役立つ場面の多くは、純粋な Python ではなく C 拡張の呼び出しが処理時間の大半を占めています。np.dot(a, b) を呼ぶと、実際の計算はコンパイル済みの C コード(今は Wasm にコンパイルされたもの)で行われます。その C コードに対する Wasm のオーバーヘッドは1.5〜3倍ですが、インタラクティブな用途なら十分許容できる範囲です。ネイティブで10ミリ秒かかる NumPy の行列積が、Pyodide では20ミリ秒になります。ノートブックで「Run」をクリックするユーザーにとっては、ほとんど気になりません。
Performance comparison (approximate):
Native CPython Pyodide (Wasm)
Pure Python loop: 1x 3-5x slower
NumPy operations: 1x 1.5-3x slower
Pandas groupby: 1x 2-3x slower
Startup time: 50ms 2-5 seconds
Package loading: instant (pip) 2-10s (download + init)
Startup is the real cost. Once loaded, interactive
performance is adequate for most use cases.
問題はむしろ起動コストです。Pyodide のコアランタイムの読み込みには10MBのダウンロードが必要です。NumPy で7MB、pandas でさらに10MBが加わります。素早く表示すべき Web ページにとって、5秒の初期化遅延は致命的です。だからこそ Pyodide は、ノートブック、データツール、インタラクティブなチュートリアルのように、ユーザーが環境の読み込みを待つことを前提とするアプリケーションで最も力を発揮します。従来型の Web ページには向きません。
Pyodide でできないこと
すべての Python パッケージが Pyodide で動くわけではありません。データベースドライバー、GUI ライブラリ、OS 固有の API を呼ぶものなど、システム依存のあるパッケージは Wasm にコンパイルできません。psycopg2 には PostgreSQL のクライアントライブラリが必要ですし、opencv にはシステムのグラフィックスライブラリが必要です。こうした依存関係はブラウザ環境には存在しません。
ネットワーク機能も制限されています。Python の socket モジュールはブラウザでは動きません。Wasm からの生のソケットアクセスはないのです。HTTP リクエストは Pyodide の JavaScript ブリッジ経由でブラウザの fetch() API を使うため、CORS の制約を受けます。Pyodide 上で Flask サーバーを動かすことはできませんし(待ち受けるソケットがないため)、任意のネットワーク接続も行えません。
スレッドは Web Worker を通じて部分的にサポートされていますが、Python の GIL(Global Interpreter Lock)は依然として適用され、スレッドの扱いもネイティブの CPython とは異なります。CPU バウンドな並列処理では、スレッドよりも multiprocessing の方が向いています。各ワーカーが独自の Wasm インスタンスを持つからです。
Pyodide とトランスパイル方式の比較
Pyodide のアプローチ(CPython を Wasm にコンパイルする)と、別の選択肢(Python を JavaScript にトランスパイルする)を比べてみる価値はあります。Transcrypt や Brython は Python の構文を JavaScript に変換し、ブラウザの JS エンジン上でネイティブに動くコードを生成します。
トランスパイルは実行時に速いです。生成された JavaScript はネイティブの JS 速度で動き、Wasm 上の CPython の速度には縛られません。また、ダウンロードするランタイムがないため、起動コストもゼロです。ただし互換性は犠牲になります。トランスパイルされた Python では C 拡張を動かせませんし、数値型、文字列処理、エッジケースなどで CPython と微妙な意味の違いが出ます。標準ライブラリもサブセットしかサポートしていません。
Pyodide の強みは、本物の CPython を動かすことです。CPython で動く Python コードは、システムレベルの依存を除けば Pyodide でも動きます。NumPy、pandas、scikit-learn に依存するデータサイエンスのワークフローでは、この互換性は譲れません。C 拡張が不要な単純なスクリプトなら、トランスパイルの方が良い選択かもしれません。Wasm とネイティブ JS の比較と同じく、適切なツールはワークロード次第です。
今後の展望
Pyodide は活発に開発が続いており、改善が進んでいます。最近のバージョンではコアランタイムのサイズが縮小され、ストリーミングコンパイル(ブラウザがダウンロードしながら Wasm をコンパイルする仕組み)によって起動時間が改善され、事前コンパイル済みパッケージの種類も増えました。WebAssembly のエコシステムも成熟してきています。WASI が標準化されたシステムインターフェースを提供し、コンポーネントモデルによって Wasm モジュール間の相互運用性もさらに向上する見込みです。
大きな流れとして、ブラウザは汎用的なランタイムになりつつあります。JavaScript、Wasm、そして Pyodide のようなプロジェクトによって、ほぼどんな言語で書かれたコードでもブラウザ上で直接動かせるようになっています。これはサーバーサイドの計算を置き換えるものではなく、サーバーを必要としない計算をクライアント側へ移すことで補完するものです。Python の巨大なデータサイエンスのエコシステムにとって、ブラウザ上でクライアント側実行できることは、これまで不可能だったユースケースを開くものです。


