Bab 37 — Python di Browser: Tanpa Server, Tanpa Pasang
Seri I5 — Aplikasi Pertama: Web, Peta, dan Dasbor
Semua skrip dan halaman demo bab ini ada di folder kode/bab37/. Di versi web, Anda juga bisa mengunduh semuanya sekaligus (zip).
Di Bab 36, setiap hitungan terjadi di server: browser meminta, server menghitung, browser menampilkan. Bayangkan penyuluh di desa yang sinyalnya putus-nyambung dan butuh kalkulator biomassa kolam. Menyalakan dan merawat server hanya untuk rumus sekecil itu terasa berlebihan. Bagaimana kalau Python ikut diunduh bersama halaman, dan semua hitungan terjadi di ponsel atau laptop pengguna? Itulah Python di browser.

WebAssembly, singkatnya
Browser aslinya hanya mengerti JavaScript. Tetapi browser modern juga bisa menjalankan WebAssembly (disingkat WASM): kode mesin kecil yang aman dan cepat. Para pengembang mengompilasi interpreter Python (CPython) menjadi WebAssembly, lalu browser menjalankannya seperti program biasa di dalam “kotak pasir” yang aman. Hasilnya:
| Nama | Apa itu |
|---|---|
| Pyodide | CPython yang dikompilasi ke WebAssembly, lengkap dengan banyak pustaka sains (NumPy dan sejenisnya) |
| PyScript | Kerangka kerja di atas Pyodide (atau MicroPython): Python ditulis langsung di halaman HTML |
| JupyterLite | JupyterLab/notebook yang seluruhnya berjalan di browser, memakai kernel Pyodide |
Pyodide: Python di dalam WebAssembly
Pyodide bisa dijalankan di browser dan juga di Node.js. Kita memakai Node.js supaya bisa menangkap keluaran nyata untuk buku ini. Skrip Python di bawah dijalankan oleh Pyodide, bukan oleh Python di komputer Anda:
01_python_di_pyodide.py
# Bab 37 - Python yang berjalan di dalam Pyodide
import platform
import sys
print("sistem:", sys.platform) # 'emscripten' = dunia WebAssembly
print("implementasi:", platform.python_implementation(), "| Python", sys.version_info[0], sys.version_info[1])
def biomassa_kg(jumlah_ikan, bobot_rata_g):
return jumlah_ikan * bobot_rata_g / 1000
print("biomassa kolam A (kg):", biomassa_kg(800, 540.5))
# Memanggil JavaScript dari Python
from js import Math
print("akar 1600 lewat JavaScript:", Math.sqrt(1600))
# NumPy tersedia sebagai paket Pyodide (dimuat dengan loadPackage di sisi JavaScript)
import numpy as np
bobot = np.array([480, 520, 505, 495, 540.5])
print("rata-rata bobot:", bobot.mean(), "| siap panen (>=500 g):", int((bobot >= 500).sum()))
# Batasan: tidak ada proses lain dan tidak ada soket jaringan biasa
import subprocess
try:
subprocess.run(["ls"])
except Exception as galat:
print("subprocess ->", type(galat).__name__)sistem: emscripten
implementasi: CPython | Python 3 14
biomassa kolam A (kg): 432.4
akar 1600 lewat JavaScript: 40
rata-rata bobot: 508.1 | siap panen (>=500 g): 3
subprocess -> OSError
Keluaran itu diambil asli sekali lewat pembungkus kecil kode/bab37/node/jalankan.mjs (pustaka pyodide versi 314.0.7 dipasang dengan npm install, dan NumPy diunduh dari CDN resmi Pyodide). Karena itu skrip diberi tanda @statis: bangun buku tidak menjalankannya ulang.
Telusuri keluarannya:
sistem: emscriptenmenandakan Python berjalan di dunia WebAssembly, bukan Windows atau Linux.- Python-nya adalah CPython 3.14, bahasa yang sama dengan buku ini. Rumus
biomassa_kgdan NumPy bekerja seperti biasa (508,1 adalah rata-rata dari lima bobot). from js import Mathmemanggil JavaScript dari Python: dua dunia bisa saling bicara.subprocessgagal denganOSError: browser tidak mengizinkan program memulai proses lain. Ini salah satu batasan kotak pasir.
Pada Pyodide, nomor versi mengikuti versi Python: seri 314 berarti Python 3.14. Kita mematok versi di alamat (.../pyodide/v314.0.7/full/pyodide.js) supaya halaman tidak tiba-tiba berubah perilakunya saat Pyodide diperbarui.
Pyodide langsung di halaman HTML
Halaman kode/bab37/demo/peta_pyodide.html memuat Pyodide dari CDN (jaringan pengantar berkas), menjalankan Python yang menggambar peta petak sederhana sebagai SVG (gambar vektor untuk web), lalu menempelkannya ke halaman. Python menghitung luas dengan rumus tali sepatu; tidak ada server, tidak ada GeoPandas. Inilah kerangkanya:
<script src="https://cdn.jsdelivr.net/pyodide/v314.0.7/full/pyodide.js"></script>
<script>
const pyodide = await loadPyodide(); // unduh dan siapkan Python
const hasil = pyodide.runPython("1 + 2"); // jalankan kode Python
await pyodide.loadPackage("numpy"); // paket Pyodide dimuat bila perlu
</script>Demo itu sudah saya coba di browser sungguhan (peta dan tabel luas tampil, dengan H1 9,00 ha, K1 4,00 ha, S1 3,00 ha, S2 2,25 ha). Untuk mencobanya sendiri, buka demo/peta_pyodide.html lewat server lokal sederhana (python -m http.server di folder demo), karena sebagian browser membatasi halaman yang dibuka langsung dari berkas.
PyScript: Python di dalam HTML
Dengan PyScript, Python ditulis langsung di dalam halaman memakai <script type="py">. Kalkulator biomassa kolam di demo/kalkulator_pyscript.html hanya butuh dua baris pemuatan:
<link rel="stylesheet" href="https://pyscript.net/releases/2026.7.3/core.css">
<script type="module" src="https://pyscript.net/releases/2026.7.3/core.js"></script>lalu Python-nya:
from pyscript import document, when
@when("click", "#hitung")
def hitung(event):
jumlah = float(document.querySelector("#jumlah").value)
bobot = float(document.querySelector("#bobot").value)
document.querySelector("#hasil").innerText = f"Biomassa: {jumlah * bobot / 1000:.1f} kg"@when("click", "#hitung") berarti: “bila tombol ber-id hitung diklik, jalankan fungsi ini”. Saya mencoba halaman itu di browser sungguhan: klik Hitung dengan isian bawaan (800 ikan, 540,5 g) menampilkan Biomassa: 432.4 kg, sama dengan hitungan Bab 6 dan 10. Tipe type="mpy" memakai MicroPython (Python mini, unduhan lebih kecil dan cepat mulai) sebagai pengganti Pyodide; pada bab ini kita hanya mencoba type="py" (Pyodide).
Skrip 02 memeriksa kedua halaman demo: semua alamat CDN-nya memuat nomor versi.
02_periksa_halaman_demo.py
# Bab 37 - Memeriksa halaman demo: versi CDN dipatok, tanpa server
import re
from pathlib import Path
demo = Path(__file__).parent / "demo"
for halaman in sorted(demo.glob("*.html")):
html = halaman.read_text(encoding="utf-8")
alamat = sorted(set(re.findall(r'(?:src|href)="(https://[^"]+)"', html)))
print(halaman.name)
for a in alamat:
print(" memuat:", a)
print(" versi dipatok (ada angka versi di alamat):", all(re.search(r"\d+\.\d+\.\d+", a) for a in alamat))kalkulator_pyscript.html
memuat: https://pyscript.net/releases/2026.7.3/core.css
memuat: https://pyscript.net/releases/2026.7.3/core.js
versi dipatok (ada angka versi di alamat): True
peta_pyodide.html
memuat: https://cdn.jsdelivr.net/pyodide/v314.0.7/full/pyodide.js
versi dipatok (ada angka versi di alamat): True
JupyterLite
JupyterLite adalah distribusi Jupyter yang seluruhnya berjalan di browser dengan kernel Pyodide. Pembuatannya memakai dua paket: jupyterlite-core (perintah jupyter lite build) dan jupyterlite-pyodide-kernel (kernel Python). Hasil build adalah situs statis yang bisa ditaruh di hosting berkas biasa, jadi cocok untuk pengajaran: murid cukup membuka tautan, tanpa memasang apa pun. Sesuai dokumentasi resmi, server yang menyajikannya harus mengirim berkas .wasm dengan jenis application/wasm.
Bagian ini tidak saya jalankan: jupyter lite build mengunduh banyak berkas kernel dan saya tidak membangunnya di komputer penulis. Versi paket yang tercatat di PyPI saat bab ini ditulis: jupyterlite-core dan jupyterlite-pyodide-kernel sama-sama 0.8.6. Cara pemakaian di atas diambil dari dokumentasi JupyterLite (halaman standalone quickstart).
Kapan cocok, kapan tidak
| Cocok | Kurang cocok |
|---|---|
| Kalkulator dan alat bantu kecil | Data besar (ratusan MB) |
| Demo dan bahan ajar tanpa pemasangan | Hitungan berat yang butuh GPU |
| Alat yang harus jalan tanpa sinyal setelah diunduh | Program yang butuh proses lain, soket jaringan, atau berkas sistem |
| Prototipe yang tidak butuh server | Rahasia: semua kode dan data ikut diunduh pengguna |
Dua catatan penting. Pertama, waktu mulai: berkas inti Pyodide berukuran belasan megabita; pada demo saya, halaman menunggu beberapa detik sebelum Python siap. Kedua, tidak ada rahasia: apa pun yang Anda taruh di halaman (kunci API, kata sandi, data pribadi) bisa dibaca siapa saja yang membukanya.
Tidak semua pustaka tersedia di Pyodide. Pustaka murni Python biasanya bisa dipasang, tetapi pustaka berkode C harus sudah dikompilasi untuk WebAssembly. Periksa daftar paket Pyodide sebelum merencanakan aplikasi.
Proyek Bersambung I5: ringkasan luas petak yang tampil di browser
Pola yang masuk akal: data disiapkan di komputer penulis dengan GeoPandas (berat), lalu hasilnya ditampilkan di browser dengan PyScript (ringan). Skrip berikut menghitung luas petak, lalu menulis main.py (dengan data tertanam) dan index.html ke _tmp/web/:
03_proyek_halaman_luas_petak.py
# Bab 37 - Proyek bersambung I5: GeoPandas menyiapkan data, browser menampilkannya dengan PyScript
import json
from pathlib import Path
import geopandas as gpd
web = Path(__file__).parent / "_tmp" / "web"
web.mkdir(parents=True, exist_ok=True)
# Langkah 1 (di komputer penulis, Python biasa): hitung luas petak dengan GeoPandas
petak = gpd.read_file("data/petak.geojson").to_crs(32748)
petak["luas_ha"] = (petak.area / 10_000).round(2)
data = petak[["kode", "sektor", "luas_ha"]].to_dict("records")
# Langkah 2: tulis program yang akan berjalan di browser (main.py) dan halamannya
(web / "main.py").write_text(f'''from pyscript import document
DATA = {json.dumps(data)}
total = {{}}
for baris in DATA:
total[baris["sektor"]] = total.get(baris["sektor"], 0) + baris["luas_ha"]
teks = "\\n".join(f"{{s:7s}} {{ha:6.2f}} ha" for s, ha in sorted(total.items()))
document.querySelector("#hasil").innerText = teks
''', encoding="utf-8")
(web / "index.html").write_text("""<!doctype html>
<html lang="id"><head><meta charset="utf-8"><title>Luas per sektor</title>
<link rel="stylesheet" href="https://pyscript.net/releases/2026.7.3/core.css">
<script type="module" src="https://pyscript.net/releases/2026.7.3/core.js"></script></head>
<body><h1>Luas per sektor</h1><pre id="hasil">menghitung...</pre>
<script type="py" src="./main.py"></script></body></html>
""", encoding="utf-8")
for berkas in sorted(web.iterdir()):
print(berkas.name, "-", berkas.stat().st_size, "byte")
print("\nisi main.py (3 baris pertama):")
print("\n".join((web / "main.py").read_text(encoding="utf-8").splitlines()[:3])[:140])index.html - 392 byte
main.py - 593 byte
isi main.py (3 baris pertama):
from pyscript import document
DATA = [{"kode": "H1", "sektor": "hutan", "luas_ha": 9.0}, {"kode": "H2", "sektor": "hutan", "luas_ha": 6.93}
Saya membuka halaman hasil itu lewat server lokal dan browser: tampil hutan 15.93 ha, kebun 7.00 ha, sawah 5.25 ha, sama dengan hitungan GeoPandas di Bab 38. Perhatikan bahwa main.py hanya memakai Python biasa: ia tidak butuh GeoPandas.
Latihan
- Buka
demo/kalkulator_pyscript.htmllewat server lokal, ubah jumlah ikan menjadi 1250, dan cocokkan hasilnya dengan hitungan tangan. - Ubah
kalkulator_pyscript.htmlagar menampilkan juga “perkiraan panen (kg)” bila 85 persen ikan hidup (angka contoh). - Tambah satu petak lagi ke dict
petakdipeta_pyodide.htmldan lihat petanya berubah. - Tantangan: buat halaman PyScript yang menghitung rata-rata bobot dari beberapa angka yang diketik pengguna dipisah koma.
Ringkasan Bab
- WebAssembly membuat Python bisa berjalan di dalam browser, dalam kotak pasir yang aman.
- Pyodide = CPython di WebAssembly; PyScript = Python di dalam HTML; JupyterLite = notebook di browser.
- Cocok untuk demo, pengajaran, dan alat kecil tanpa server; tidak cocok untuk data besar, rahasia, atau program yang butuh proses lain.
- Patok nomor versi pada alamat CDN. Kode dan data ikut terunduh oleh pengguna.
Selanjutnya, di Bab 38: data peta pertama Anda: titik, garis, dan poligon dengan GeoPandas.