I Built a Python IDE That Runs Entirely in Your Browser Using WebAssembly
"Your Browser. Your Rules. Your Python."
If you've ever thought "I just want to run this one Python snippet" and then spent the next 15 minutes arguing with your environment instead ā this post is for you.
I built PythCode: a browser-native Python playground that runs CPython 3.11 directly inside your browser tab using WebAssembly. No install. No server. No account. No code leaves your machine.
Let me walk you through what I built, how it works under the hood, and some of the tricky technical problems I had to solve along the way.
š What Is PythCode?
PythCode is a single-page application that gives you a full Python development environment inside your browser. It looks and feels like a lightweight IDE ā syntax highlighting, line numbers, error detection, a console with coloured output, and even interactive plots.
The key difference from most browser-based Python tools: everything executes locally. Your Python code never leaves your device.
Here's what the stack looks like from 10,000 feet:
Browser Tab
āāā HTML/CSS/JS (no framework, no build step)
āāā Pyodide 0.25 (CPython 3.11 ā WebAssembly)
ā āāā NumPy
ā āāā Pandas
ā āāā Matplotlib
ā āāā Seaborn (via micropip)
āāā Custom editor engine
āāā Syntax highlighter (vanilla JS)
āāā Live error overlay
āāā Async input() bridge
⨠Features at a Glance
Before I get into the technical bits, here's a quick rundown of what PythCode can do:
- Real Python 3.11 ā not a transpiler, not a subset, actual CPython in WASM
- NumPy, Pandas, Matplotlib, Seaborn preloaded and ready
- Live syntax highlighting with per-token coloring
- Red squiggly underlines on syntax errors as you type
-
input()that actually works ā execution pauses, user types, execution resumes - Inline plot viewer ā matplotlib/seaborn charts render inside a modal
- Auto-saves to localStorage ā code persists across sessions
- Share via URL ā code is base64-encoded into the link, no backend needed
- Three themes ā Tokyo Night, Dark, White
- Zero tracking. Zero ads. Zero accounts.
Code is auto-saved with a 400ms debounce. Theme, font, and zoom levels are saved on change. Everything is restored synchronously on init() before Pyodide even starts loading ā so the editor has your last session visible before the Python runtime is ready.
š¤ Challenges and Lessons Learned
1. color: transparent kills text-decoration in Chromium.
The squiggly underlines simply didn't appear. Spent longer than I'd like to admit on this. Solution: rgba(0,0,0,0) instead of transparent.
2. run_sync doesn't exist in Pyodide 0.25.
Early versions of my async input() implementation used pyodide.ffi.run_sync, which doesn't exist in this version. The correct solution was the async def _run() wrapper combined with Pyodide's native runPythonAsync.
3. exec() returns values as Python proxies.
When I first ran user code with exec(user_code), the last expression's value would appear as [object Object] in the console because Pyodide returned a Python proxy. Wrapping everything in async def _run(): ... means the function returns None, and nothing unexpected appears in the output.
4. Seaborn isn't bundled in Pyodide.
loadPackage('seaborn') throws. The solution is micropip.install('seaborn', keep_going=True) ā but this needs network access from inside WASM, which works in modern browsers but can fail in restrictive environments. I handle this gracefully: seaborn failing doesn't block the rest of the app from loading.
š What's Next
- Mobile layout ā vertical split with a touch-friendly drag divider
- File upload ā drag a CSV directly into the Python environment
- More libraries ā SciPy, Sympy, Statsmodels
- Code history ā timeline of past runs per session
-
Export to Jupyter ā download your code as a
.ipynb
š ļø The Stack Summary
| Thing | How |
|---|---|
| Python runtime | Pyodide 0.25 (CPython 3.11 ā WASM) |
| UI framework | None (vanilla HTML/CSS/JS) |
| Syntax highlighting | Custom tokeniser, ~150 lines of JS |
| Package manager | micropip (for packages not bundled in Pyodide) |
| Storage | localStorage only |
| Build step | None ā single .html file |
| Bundle size | ~1.8 MB HTML + Pyodide loads on demand |
Final Thoughts
PythCode started as a solution to my own frustration. It turned into a genuinely interesting engineering challenge ā threading together WebAssembly, async JavaScript, Python coroutines, and CSS rendering quirks into something that just feels like an IDE.
The thing I'm most proud of isn't any individual feature. It's that the whole thing is a single HTML file you can save to your desktop and open offline, and it still works.
If you try it, I'd love to hear what you think ā especially if you break something.
Built by **Hasan Aflatoon**
Tags: python webassembly opensource programming javascript tutorial devtools
Top comments (1)
link?!