Show and Tell - WebDash (Dash running entirely in the browser)

Hello Dashers!

Today I wanted to share with you a research project I worked on earlier this year: running Dash entirely in the browser, as a static website and without the need for a local Python environment.

This project is still a proof of concept so not everything works, but please do feel free to post issues on the GitHub repo or reach out directly!

Blog post: https://polyglot.codes/posts/webdash
GitHub: GitHub - ibdafna/webdash: WebAssembly powered Plotly Dash running in the browser

Thanks!

17 Likes

Haha, this is so cool and so crazy. I will definitely keep following :smiley:

EDIT: I seems that the demo is stuck downloading numpy.js for some reason.

Hey! Thanks for trying it out. I’m currently serving the demo from Vercel and Chrome seems to have an issue with their edge network when served large files. They’re aware of the issue. If you have Firefox installed, it should work there :blush:

I’ll probably migrate to a different host if they don’t fix the issue in the next few days.

But isn’t it like 200 kB? That shouldn’t be a problem…

numpy.js is actually a bootstrapper which loads numpy.data, which is around 8.5mb. Also, the issue with Vercel is not deterministic - next time numpy could load, but pandas won’t. Try with Firefox if you want to use the demo link, otherwise Chrome works perfectly fine - just clone and run locally. Let me know how it goes!

EDIT: I set up additional hosting with Netlify. It seems to work as expected with Chrome! :relaxed:

Updated the README on the GitHub page to reflect the Netlify deployment address.

Incredible stuff!

How does data loading work?

  • pd.read_csv - Will this bundle the CSV and ship it to the browser or do you need to include it separately?
  • Database queries - I’m assuming these won’t work since browsers can’t connect directly to databases
  • Network requests - Are requests.get translated to fetch calls?

Curious if you’ve explored mixing and matching server and clientside with the WASM stack - So you could use Python on the server for all of the data fetching callbacks but then do the analytics and data processing in the client with WASM.

:wave: Hey @chriddyp!

Generally speaking, there is no native networking in WASM-land, and so the solution is to expose the JavaScript networking APIs and use them for data retrieval. These include WebSocket and XMLHttpRequest/Fetch.

Specifically, in Pyodide, which is CPython compiled to WASM, we have Python bindings to those JavaScript APIs. So we could have something like that:

# Taken from https://github.com/pyodide/pyodide/blob/b19e2762a851a679cf809ac582c753d5c9c0bb0d/src/py/pyodide/_base.py#L15
def open_url(url: str) -> StringIO:
    """
    Fetches a given URL
    Parameters
    ----------
    url : str
       URL to fetch
    Returns
    -------
    io.StringIO
        the contents of the URL.
    """
    from js import XMLHttpRequest

    req = XMLHttpRequest.new()
    req.open("GET", url, False)
    req.send(None)
    return StringIO(req.response)

This allows us to write something like that when retrieving data for use in pd.read_csv:

data = pyodide.open_url('https://plotly.github.io/datasets/country_indicators.csv')
df = pd.read_csv(data)

In fact, that’s exactly what we’re doing in the adapted Dash demo app you wrote!

pd.read_csv and many other libraries rely on urllib. In the current configuration both urllib and requests calls will result in no-ops. With that said, it should be possible to monkey-patch urllib so that it’s using the js API introduced above for any HTTP requests (this should also apply to requests.get). If we manage to get that to work then we will be able to eliminate the friction of adding pyodide.open() and be able to use pd.read_csv with an address directly.

For database queries, native socket connections will not work out of the box unfortunately (for the same reasons mentioned above). But the good news is you can still use some databases which support WebSocket connections. And for those which do not, it is possible to use a gateway server which receives WebSocket connections and relays them as regular socket connections. There’s an overhead there which is not ideal, but it’s a solution.

As for the last point about mixing and matching server/client on the WASM stack - it’s such an exciting prospect! Because Pyodide is essentially CPython running in WASM, everything (including the server) is essentially pushed on to the WASM stack to begin with - packages like Pandas and Numpy are compiled to WASM and run inside the browser. This allows minimally modified Dash apps to “just work” in the browser.

We can bundle multiple WASM modules, so anything in addition to the Python kernel, - although admittedly I did not experiment with that! Perhaps there’s scope to add something to Dash Renderer? I would love to hear any feedback or ideas you may have :blush:,

2 Likes