A private browser Python notebook with self-hosted Pyodide, persistent execution state, structured outputs, and portable export.
- Real CPython WASM Runtime: Executes Python through Pyodide rather than translating or simulating Python in JavaScript.
- Dedicated Web Worker: Keeps runtime compilation and cell execution away from the main UI thread.
- Self-Hosted Runtime: Serves the Pyodide loader, lock file, WebAssembly binary, and Python standard library from
public/pyodidewithout a runtime CDN dependency. - Persistent Python Namespace: Preserves imports, functions, and variables between cell executions until the runtime is explicitly reset.
- Ordered Run All: Queues notebook cells and executes them sequentially against the shared Python namespace.
- Cell Operations: Adds, edits, duplicates, reorders, runs, and deletes cells with stable execution metadata.
- Structured Output: Renders standard output, standard error, scalar values, dictionaries, record tables, and lightweight bar or line charts.
- Runtime Variable Inspector: Displays user-created namespace values with Python types and compact previews.
- Python Import: Converts an imported
.pyfile into a new executable notebook cell. - Portable Notebook Export: Downloads the current workspace as a valid
.ipynbdocument. - Local Notebook Persistence: Stores notebook cells in IndexedDB through
idb-keyvalwithout a backend. - Runtime Lifecycle Controls: Shows warmup and CPython status, reports execution timing, and supports a complete worker reset.
- Responsive Editing Surface: Keeps cell actions, code editors, outputs, and runtime panels usable on mobile while favoring wide screens for longer code.
public/
|-- favicon.svg
|-- sample-analysis.py
`-- pyodide/
|-- pyodide-lock.json
|-- pyodide.asm.mjs
|-- pyodide.asm.wasm
`-- python_stdlib.zip
src/
|-- app.tsx # notebook state, execution queue, import, export, and persistence
|-- main.tsx
|-- index.css
|-- components/
| |-- cell-card.tsx # code cell controls, execution state, and output shell
| `-- output-view.tsx # text, error, table, scalar, and chart renderers
|-- domain/
| `-- types.ts # notebook, output, variable, and worker message contracts
`-- workers/
`-- python.worker.ts # Pyodide initialization and browser-local Python execution
- Preact 10 with Vite 8
- TypeScript 6
- Tailwind CSS 4
pyodide- Web Workers
idb-keyvallucide-preact
- The starter notebook contains three cells covering descriptive statistics, a list-of-records table, and a chart result contract.
public/sample-analysis.pyprovides a standalone Python file for testing the import workflow.public/pyodidecontains the matching loader, lock manifest, WASM runtime, and standard library used by the worker.- The examples require no third-party Python package installation and work with the bundled standard library.
The final Python expression becomes the cell result. A list of dictionaries renders as a table. A dictionary in this form renders as a chart:
{
"type": "bar",
"title": "Requests by service",
"x": ["API", "Worker", "Search"],
"y": [120, 88, 64],
}Set type to "line" to render a line chart with the same x and y arrays.
npm install
npm run devOpen http://localhost:5173.
- Python code, outputs, variables, and imported files remain inside the browser and are never submitted to an application server.
- Pyodide performs a real CPython WASM startup, so the first cold load can take longer than later cached sessions.
- The worker keeps one shared namespace, which gives notebook cells familiar stateful behavior and also means execution order matters.
- Reset Python runtime terminates the current worker and removes all in-memory Python variables before starting a fresh runtime.
stdoutandstderrare captured per execution and transferred to the UI through typed worker messages.- PyProxy values are converted to serializable JavaScript structures before leaving the worker and are destroyed after conversion.
- The included runtime provides Python's standard library; packages such as pandas, NumPy, or Matplotlib are not preinstalled by this project.
- Chart rendering intentionally uses a compact local SVG renderer instead of requiring a heavy plotting framework.
- IndexedDB persistence stores the notebook document, not the active Python heap or imported module state.
- The UI is responsive, but editing long Python cells and comparing multiple outputs is naturally more comfortable on desktop.
npm run build
npm run previewThe production build validates TypeScript before emitting the Preact application and dedicated Python worker. Keep the files in public/pyodide version-aligned when upgrading the pyodide package.
The repository includes a Vite-ready vercel.json with SPA rewrites.
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"framework": "vite",
"buildCommand": "npm run build",
"outputDirectory": "dist",
"rewrites": [
{
"source": "/(.*)",
"destination": "/index.html"
}
]
}MIT License. See LICENSE. Bundled Pyodide notices are documented in THIRD_PARTY_LICENSES.md.