<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hasan Aflatoon</title>
    <description>The latest articles on DEV Community by Hasan Aflatoon (@hasan_53_52).</description>
    <link>https://dev.to/hasan_53_52</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3904621%2F1d6a7942-911c-4297-badc-bb64612d47b9.jpg</url>
      <title>DEV Community: Hasan Aflatoon</title>
      <link>https://dev.to/hasan_53_52</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hasan_53_52"/>
    <language>en</language>
    <item>
      <title>PythCode: A Zero-Install Python IDE Running Fully in the Browser</title>
      <dc:creator>Hasan Aflatoon</dc:creator>
      <pubDate>Wed, 29 Apr 2026 15:25:53 +0000</pubDate>
      <link>https://dev.to/hasan_53_52/pythcode-a-zero-install-python-ide-running-fully-in-the-browser-26jm</link>
      <guid>https://dev.to/hasan_53_52/pythcode-a-zero-install-python-ide-running-fully-in-the-browser-26jm</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Python IDE That Runs Entirely in Your Browser Using WebAssembly
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Your Browser. Your Rules. Your Python."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you've ever thought &lt;em&gt;"I just want to run this one Python snippet"&lt;/em&gt; and then spent the next 15 minutes arguing with your environment instead — this post is for you.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;PythCode&lt;/strong&gt;: 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;h2&gt;
  
  
  🐍 What Is PythCode?
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The key difference from most browser-based Python tools: &lt;strong&gt;everything executes locally&lt;/strong&gt;. Your Python code never leaves your device.&lt;/p&gt;

&lt;p&gt;Here's what the stack looks like from 10,000 feet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Link to PythCode: &lt;a href="https://pythcode.netlify.app/" rel="noopener noreferrer"&gt;PythCode: Unleash your Code!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ Features at a Glance
&lt;/h2&gt;

&lt;p&gt;Before I get into the technical bits, here's a quick rundown of what PythCode can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real Python 3.11&lt;/strong&gt; — not a transpiler, not a subset, actual CPython in WASM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NumPy, Pandas, Matplotlib, Seaborn&lt;/strong&gt; preloaded and ready&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live syntax highlighting&lt;/strong&gt; with per-token coloring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red squiggly underlines&lt;/strong&gt; on syntax errors as you type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;input()&lt;/code&gt; that actually works&lt;/strong&gt; — execution pauses, user types, execution resumes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline plot viewer&lt;/strong&gt; — matplotlib/seaborn charts render inside a modal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-saves to localStorage&lt;/strong&gt; — code persists across sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share via URL&lt;/strong&gt; — code is base64-encoded into the link, no backend needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three themes&lt;/strong&gt; — Tokyo Night, Dark, White&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero tracking. Zero ads. Zero accounts.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Code is auto-saved with a 400ms debounce. Theme, font, and zoom levels are saved on change. Everything is restored synchronously on &lt;code&gt;init()&lt;/code&gt; before Pyodide even starts loading — so the editor has your last session visible before the Python runtime is ready.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 Challenges and Lessons Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;color: transparent&lt;/code&gt; kills text-decoration in Chromium.&lt;/strong&gt;&lt;br&gt;
The squiggly underlines simply didn't appear. Spent longer than I'd like to admit on this. Solution: &lt;code&gt;rgba(0,0,0,0)&lt;/code&gt; instead of &lt;code&gt;transparent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;run_sync&lt;/code&gt; doesn't exist in Pyodide 0.25.&lt;/strong&gt;&lt;br&gt;
Early versions of my async &lt;code&gt;input()&lt;/code&gt; implementation used &lt;code&gt;pyodide.ffi.run_sync&lt;/code&gt;, which doesn't exist in this version. The correct solution was the &lt;code&gt;async def _run()&lt;/code&gt; wrapper combined with Pyodide's native &lt;code&gt;runPythonAsync&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;exec()&lt;/code&gt; returns values as Python proxies.&lt;/strong&gt;&lt;br&gt;
When I first ran user code with &lt;code&gt;exec(user_code)&lt;/code&gt;, the last expression's value would appear as &lt;code&gt;[object Object]&lt;/code&gt; in the console because Pyodide returned a Python proxy. Wrapping everything in &lt;code&gt;async def _run(): ...&lt;/code&gt; means the function returns &lt;code&gt;None&lt;/code&gt;, and nothing unexpected appears in the output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Seaborn isn't bundled in Pyodide.&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;loadPackage('seaborn')&lt;/code&gt; throws. The solution is &lt;code&gt;micropip.install('seaborn', keep_going=True)&lt;/code&gt; — 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.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mobile layout&lt;/strong&gt; — vertical split with a touch-friendly drag divider&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File upload&lt;/strong&gt; — drag a CSV directly into the Python environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More libraries&lt;/strong&gt; — SciPy, Sympy, Statsmodels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code history&lt;/strong&gt; — timeline of past runs per session&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export to Jupyter&lt;/strong&gt; — download your code as a &lt;code&gt;.ipynb&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ The Stack Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Thing&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Python runtime&lt;/td&gt;
&lt;td&gt;Pyodide 0.25 (CPython 3.11 → WASM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI framework&lt;/td&gt;
&lt;td&gt;None (vanilla HTML/CSS/JS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Syntax highlighting&lt;/td&gt;
&lt;td&gt;Custom tokeniser, ~150 lines of JS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package manager&lt;/td&gt;
&lt;td&gt;micropip (for packages not bundled in Pyodide)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;localStorage only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build step&lt;/td&gt;
&lt;td&gt;None — single &lt;code&gt;.html&lt;/code&gt; file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bundle size&lt;/td&gt;
&lt;td&gt;~1.8 MB HTML + Pyodide loads on demand&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;If you try it, I'd love to hear what you think — especially if you break something.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by **Hasan Aflatoon&lt;/em&gt;**&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: &lt;code&gt;python&lt;/code&gt; &lt;code&gt;webassembly&lt;/code&gt; &lt;code&gt;opensource&lt;/code&gt; &lt;code&gt;programming&lt;/code&gt; &lt;code&gt;javascript&lt;/code&gt; &lt;code&gt;tutorial&lt;/code&gt; &lt;code&gt;devtools&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>showdev</category>
      <category>tooling</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Built a Python IDE That Runs Entirely in Your Browser Using WebAssembly</title>
      <dc:creator>Hasan Aflatoon</dc:creator>
      <pubDate>Wed, 29 Apr 2026 15:20:40 +0000</pubDate>
      <link>https://dev.to/hasan_53_52/i-built-a-python-ide-that-runs-entirely-in-your-browser-using-webassembly-111e</link>
      <guid>https://dev.to/hasan_53_52/i-built-a-python-ide-that-runs-entirely-in-your-browser-using-webassembly-111e</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Python IDE That Runs Entirely in Your Browser Using WebAssembly
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Your Browser. Your Rules. Your Python."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you've ever thought &lt;em&gt;"I just want to run this one Python snippet"&lt;/em&gt; and then spent the next 15 minutes arguing with your environment instead — this post is for you.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;PythCode&lt;/strong&gt;: 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;h2&gt;
  
  
  🐍 What Is PythCode?
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The key difference from most browser-based Python tools: &lt;strong&gt;everything executes locally&lt;/strong&gt;. Your Python code never leaves your device.&lt;/p&gt;

&lt;p&gt;Here's what the stack looks like from 10,000 feet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Link: &lt;a href="//PythCode.netlify.app"&gt;PythCode: Unleash your Code!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ Features at a Glance
&lt;/h2&gt;

&lt;p&gt;Before I get into the technical bits, here's a quick rundown of what PythCode can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real Python 3.11&lt;/strong&gt; — not a transpiler, not a subset, actual CPython in WASM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NumPy, Pandas, Matplotlib, Seaborn&lt;/strong&gt; preloaded and ready&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live syntax highlighting&lt;/strong&gt; with per-token coloring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red squiggly underlines&lt;/strong&gt; on syntax errors as you type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;input()&lt;/code&gt; that actually works&lt;/strong&gt; — execution pauses, user types, execution resumes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline plot viewer&lt;/strong&gt; — matplotlib/seaborn charts render inside a modal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-saves to localStorage&lt;/strong&gt; — code persists across sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share via URL&lt;/strong&gt; — code is base64-encoded into the link, no backend needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three themes&lt;/strong&gt; — Tokyo Night, Dark, White&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero tracking. Zero ads. Zero accounts.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Code is auto-saved with a 400ms debounce. Theme, font, and zoom levels are saved on change. Everything is restored synchronously on &lt;code&gt;init()&lt;/code&gt; before Pyodide even starts loading — so the editor has your last session visible before the Python runtime is ready.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 Challenges and Lessons Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;color: transparent&lt;/code&gt; kills text-decoration in Chromium.&lt;/strong&gt;&lt;br&gt;
The squiggly underlines simply didn't appear. Spent longer than I'd like to admit on this. Solution: &lt;code&gt;rgba(0,0,0,0)&lt;/code&gt; instead of &lt;code&gt;transparent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;run_sync&lt;/code&gt; doesn't exist in Pyodide 0.25.&lt;/strong&gt;&lt;br&gt;
Early versions of my async &lt;code&gt;input()&lt;/code&gt; implementation used &lt;code&gt;pyodide.ffi.run_sync&lt;/code&gt;, which doesn't exist in this version. The correct solution was the &lt;code&gt;async def _run()&lt;/code&gt; wrapper combined with Pyodide's native &lt;code&gt;runPythonAsync&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;exec()&lt;/code&gt; returns values as Python proxies.&lt;/strong&gt;&lt;br&gt;
When I first ran user code with &lt;code&gt;exec(user_code)&lt;/code&gt;, the last expression's value would appear as &lt;code&gt;[object Object]&lt;/code&gt; in the console because Pyodide returned a Python proxy. Wrapping everything in &lt;code&gt;async def _run(): ...&lt;/code&gt; means the function returns &lt;code&gt;None&lt;/code&gt;, and nothing unexpected appears in the output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Seaborn isn't bundled in Pyodide.&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;loadPackage('seaborn')&lt;/code&gt; throws. The solution is &lt;code&gt;micropip.install('seaborn', keep_going=True)&lt;/code&gt; — 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.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mobile layout&lt;/strong&gt; — vertical split with a touch-friendly drag divider&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File upload&lt;/strong&gt; — drag a CSV directly into the Python environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More libraries&lt;/strong&gt; — SciPy, Sympy, Statsmodels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code history&lt;/strong&gt; — timeline of past runs per session&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export to Jupyter&lt;/strong&gt; — download your code as a &lt;code&gt;.ipynb&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ The Stack Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Thing&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Python runtime&lt;/td&gt;
&lt;td&gt;Pyodide 0.25 (CPython 3.11 → WASM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI framework&lt;/td&gt;
&lt;td&gt;None (vanilla HTML/CSS/JS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Syntax highlighting&lt;/td&gt;
&lt;td&gt;Custom tokeniser, ~150 lines of JS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package manager&lt;/td&gt;
&lt;td&gt;micropip (for packages not bundled in Pyodide)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;localStorage only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build step&lt;/td&gt;
&lt;td&gt;None — single &lt;code&gt;.html&lt;/code&gt; file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bundle size&lt;/td&gt;
&lt;td&gt;~1.8 MB HTML + Pyodide loads on demand&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;If you try it, I'd love to hear what you think — especially if you break something.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by **Hasan Aflatoon&lt;/em&gt;**&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: &lt;code&gt;python&lt;/code&gt; &lt;code&gt;webassembly&lt;/code&gt; &lt;code&gt;opensource&lt;/code&gt; &lt;code&gt;programming&lt;/code&gt; &lt;code&gt;javascript&lt;/code&gt; &lt;code&gt;tutorial&lt;/code&gt; &lt;code&gt;devtools&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>webassembly</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
