Pyodide: Python reale nel browser con WebAssembly
Pyodide compila CPython in WebAssembly ed esegue Python con NumPy e pandas direttamente nel browser. Come funziona e quando conviene usarlo.

Python nel browser è un sogno dai primi anni 2010, e ogni tentativo ha avuto lo stesso problema: Python è troppo legato al runtime C di CPython per essere portato facilmente su un ambiente web. Transcrypt, Brython e Skulpt hanno cercato di risolvere la questione reimplementando Python in JavaScript, cosa che funzionava con script semplici ma andava in crisi appena servivano NumPy, pandas o una delle librerie con estensioni C che rendono Python davvero utile.
Pyodide segue un approccio diverso: compilare l'interprete CPython reale in WebAssembly. Non una reimplementazione, ma il codice sorgente vero di CPython, compilato con Emscripten per girare nel browser. Questo significa piena compatibilità con il linguaggio, incluse le estensioni C. Puoi fare import numpy e funziona, perché il binario Wasm compilato include il codice C vero di NumPy.
Come funziona Pyodide sotto il cofano
Il processo di build di Pyodide prende l'albero dei sorgenti di CPython e lo compila con Emscripten, una toolchain che punta a WebAssembly invece che al codice macchina nativo. Il risultato è un binario .wasm che implementa l'interprete Python, più il codice JavaScript di collegamento che lo connette all'ambiente del browser.
La parte complicata sono le estensioni C. NumPy, per esempio, contiene oltre 100.000 righe di codice C e Fortran. Pyodide precompila un insieme selezionato di pacchetti popolari — NumPy, pandas, scikit-learn, matplotlib, scipy — in moduli Wasm che vengono caricati su richiesta. Quando chiami import numpy, Pyodide scarica il modulo Wasm precompilato di NumPy, lo collega all'interprete in esecuzione e lo inizializza.
<!-- 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>
Il ponte tra JavaScript e Python
Una delle caratteristiche migliori di Pyodide è l'interoperabilità trasparente tra Python e JavaScript. Gli oggetti Python vengono automaticamente esposti come proxy in JavaScript e viceversa. Puoi chiamare funzioni JavaScript da Python, manipolare il DOM da Python e passare dati tra i due linguaggi senza serializzazione manuale.
// 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!'
`);
Il sistema di proxy gestisce la conversione dei tipi in automatico: i dict Python diventano oggetti JavaScript, le liste Python diventano array JavaScript e i numeri Python diventano numeri JavaScript. Per trasferimenti di dati grandi — come passare un array NumPy da un milione di elementi a una libreria di visualizzazione JavaScript — Pyodide usa buffer di memoria condivisa per evitare copie, cosa fondamentale per le prestazioni.
Quando ha senso usare Pyodide
Ambienti di calcolo interattivi. JupyterLite, costruito su Pyodide, esegue notebook Jupyter interamente nel browser. Non serve un server: il kernel Python gira in locale grazie a Wasm. Per la didattica è una svolta: gli studenti possono eseguire notebook Python senza installare nulla, e il docente non deve configurare server.
Strumenti di esplorazione dei dati. Le applicazioni web che permettono agli utenti di caricare dati e analizzarli possono usare Pyodide per elaborare tutto lato client. I dati non lasciano il browser, il che risolve problemi di privacy ed elimina i costi del server. Un uploader di CSV che esegue trasformazioni con pandas nel browser è più semplice da distribuire e più privato di uno che invia i dati a un backend.
Documentazione con esempi live. La documentazione delle librerie Python può includere blocchi di codice interattivi che vengono davvero eseguiti. Invece di mostrare un output statico, il lettore può modificare il codice e vedere subito il risultato. Per l'apprendimento è molto più efficace degli esempi statici.
Prototipazione e sperimentazione. I data scientist che ragionano in Python possono prototipare trasformazioni e visualizzazioni direttamente nel browser, e poi condividere il risultato con un URL. Niente setup dell'ambiente, niente gestione delle dipendenze, niente problemi del tipo 'funziona sulla mia macchina'.
La realtà delle prestazioni
Pyodide non è veloce. WebAssembly ha un overhead rispetto al codice nativo — tipicamente da 1,5 a 3 volte più lento sui carichi di lavoro pesanti in calcolo. In più, Pyodide esegue CPython (già 100 volte più lento del C per il codice Python puro) compilato in Wasm. Per i cicli Python puri, Pyodide è circa 2-5 volte più lento del CPython nativo.
Ma il punto è questo: i carichi di lavoro in cui Pyodide è utile sono dominati dalle chiamate alle estensioni C, non dal Python puro. Quando chiami np.dot(a, b), il calcolo vero avviene in codice C compilato (ora in Wasm). L'overhead di Wasm su quel codice C è 1,5-3x, che per casi d'uso interattivi va più che bene. Una moltiplicazione di matrici NumPy che richiede 10 ms nativamente ne richiede 20 in Pyodide. Per un utente che clicca 'Run' in un notebook, è impercettibile.
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.
Il vero problema è il tempo di avvio. Il runtime di base di Pyodide pesa 10 MB da scaricare. NumPy aggiunge altri 7 MB, pandas altri 10 MB. Per una pagina web che deve caricarsi in fretta, un ritardo di inizializzazione di 5 secondi è un deal-breaker. Per questo Pyodide funziona meglio in applicazioni in cui l'utente si aspetta di attendere il caricamento di un ambiente — notebook, strumenti sui dati, tutorial interattivi — piuttosto che nelle classiche pagine web.
Cosa Pyodide non può fare
Non tutti i pacchetti Python funzionano in Pyodide. I pacchetti con dipendenze di sistema — driver di database, librerie GUI, tutto ciò che chiama API specifiche del sistema operativo — non possono essere compilati in Wasm. psycopg2 richiede una libreria client di PostgreSQL. opencv richiede le librerie grafiche di sistema. Queste dipendenze nel browser non esistono.
Anche la rete è limitata. Il modulo socket di Python non funziona nel browser: da Wasm non c'è accesso diretto ai socket. Le richieste HTTP passano per la API fetch() del browser tramite il bridge JavaScript di Pyodide, quindi sono soggette alle restrizioni CORS. Non puoi far girare un server Flask in Pyodide (non c'è un socket su cui mettersi in ascolto) e non puoi aprire connessioni di rete arbitrarie.
Il threading è supportato in parte tramite i Web Worker, ma il GIL (Global Interpreter Lock) di Python si applica comunque, e il modello di threading differisce da CPython nativo. Il parallelismo per i carichi legati alla CPU funziona meglio con il multiprocessing (ogni worker ha la propria istanza Wasm) che con i thread.
Pyodide e il Python transpilato
Vale la pena confrontare l'approccio di Pyodide (compilare CPython in Wasm) con l'alternativa (transpilare Python in JavaScript). Progetti come Transcrypt e Brython convertono la sintassi Python in JavaScript, producendo codice che gira nativamente nel motore JS del browser.
La transpilazione è più veloce in esecuzione: il JavaScript generato gira alla velocità del JS nativo, non a quella di CPython in Wasm. Ha anche costo di avvio zero, perché non c'è un runtime da scaricare. Però sacrifica la compatibilità: il Python transpilato non può eseguire estensioni C, presenta sottili differenze semantiche rispetto a CPython (soprattutto su tipi numerici, gestione delle stringhe e casi limite) e supporta solo un sottoinsieme della libreria standard.
Il vantaggio di Pyodide è che esegue il CPython vero. Se il tuo codice Python funziona in CPython, funziona in Pyodide (a parte le dipendenze a livello di sistema). Per i flussi di lavoro di data science che dipendono da NumPy, pandas e scikit-learn, questa compatibilità non è negoziabile. Per script semplici che non hanno bisogno di estensioni C, la transpilazione può essere la scelta migliore — come in ogni confronto tra Wasm e JavaScript nativo, lo strumento giusto dipende dal carico di lavoro.
Dove sta andando tutto questo
Pyodide è in sviluppo attivo e sta migliorando. Le versioni recenti hanno ridotto la dimensione del runtime di base, migliorato i tempi di avvio con la compilazione in streaming (il browser compila il Wasm mentre lo scarica) ed esteso l'insieme dei pacchetti precompilati. Anche l'ecosistema WebAssembly sta maturando: WASI fornisce interfacce di sistema standardizzate e il component model permetterà un'interoperabilità migliore tra moduli Wasm.
La tendenza più ampia è che il browser stia diventando un runtime universale. Tra JavaScript, Wasm e progetti come Pyodide, puoi eseguire codice scritto in quasi qualsiasi linguaggio direttamente nel browser. Questo non sostituisce il calcolo lato server: lo completa, spostando sul client la computazione che non ha bisogno di un server. Per il vasto ecosistema di data science di Python, eseguire codice nel browser apre scenari che prima non erano semplicemente possibili.


