Bab 37 — Python di Browser: Tanpa Server, Tanpa Pasang

Seri I5 — Aplikasi Pertama: Web, Peta, dan Dasbor

CatatanDi bab ini, Anda akan:
  • Memahami bagaimana Python bisa berjalan di dalam browser lewat WebAssembly
  • Menjalankan kode Python di Pyodide dan memahami batasnya
  • Membuat halaman PyScript yang interaktif tanpa server
  • Mengenal JupyterLite (notebook yang berjalan di browser)
  • Menilai kapan cara ini cocok, dan kapan tidak

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.

Kiri-kanan: cara biasa (hitungan di server) dan Python di browser (hitungan di komputer pengguna).

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: emscripten menandakan Python berjalan di dunia WebAssembly, bukan Windows atau Linux.
  • Python-nya adalah CPython 3.14, bahasa yang sama dengan buku ini. Rumus biomassa_kg dan NumPy bekerja seperti biasa (508,1 adalah rata-rata dari lima bobot).
  • from js import Math memanggil JavaScript dari Python: dua dunia bisa saling bicara.
  • subprocess gagal dengan OSError: browser tidak mengizinkan program memulai proses lain. Ini salah satu batasan kotak pasir.
CatatanINGAT

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.

PeringatanAWAS

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

  1. Buka demo/kalkulator_pyscript.html lewat server lokal, ubah jumlah ikan menjadi 1250, dan cocokkan hasilnya dengan hitungan tangan.
  2. Ubah kalkulator_pyscript.html agar menampilkan juga “perkiraan panen (kg)” bila 85 persen ikan hidup (angka contoh).
  3. Tambah satu petak lagi ke dict petak di peta_pyodide.html dan lihat petanya berubah.
  4. 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.