DEV Community

Cover image for I Built a Python IDE That Runs Entirely in Your Browser Using WebAssembly
Hasan Aflatoon
Hasan Aflatoon

Posted on

I Built a Python IDE That Runs Entirely in Your Browser Using WebAssembly

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
Enter fullscreen mode Exit fullscreen mode

✨ 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)

Collapse
 
embernoglow profile image
EmberNoGlow

link?!