This project demonstrates two different architectural approaches for creating data visualizations using Python in the browser via Pyodide and Plotly.
How do you create sophisticated data visualizations in a browser environment using Python without backend infrastructure? Two main approaches emerge:
- Full Python Stack: Python handles both computation AND visualization
- Hybrid Approach: Python for computation, JavaScript for visualization
pyodide-plotly-sankey-demo.html- Approach A: Full Python stackpyodide-js-plotly-demo.html- Approach B: Hybrid approachREADME.md- This documentation
File: pyodide-plotly-sankey-demo.html
Browser β Pyodide β Python Plotly β HTML/JS Rendering
- Loads Pyodide (Python runtime for browser)
- Installs Python Plotly via micropip
- Python code creates Plotly charts using
plotly.graph_objects - Python generates complete chart configuration as JSON
- JavaScript receives the chart config and renders with Plotly.js
# Python handles everything
import plotly.graph_objects as go
# Create chart in Python
fig = go.Figure(data=go.Sankey(...))
chart_json = fig.to_json() # Complete chart config// JavaScript just renders
const chartConfig = JSON.parse(pythonChartJson);
Plotly.newPlot('div', chartConfig.data, chartConfig.layout);- β Pure Python workflow - familiar for Python developers
- β Complete Plotly Python API available
- β Complex chart configurations handled in Python
- β Reliability issues: Pyodide + Python Plotly integration can be fragile
- β Larger bundle: Must load full Python Plotly package (~several MB)
- β Slower startup: More packages to install via micropip
- β Debugging complexity: Errors can occur in Python Plotly β JSON β JS chain
- Projects where Python Plotly's advanced features are essential
- Teams with strong Python expertise but limited JavaScript knowledge
- Complex statistical plots that leverage Python Plotly's specialized functions
File: pyodide-js-plotly-demo.html
Browser β Pyodide β Python (data processing) β JSON β JavaScript Plotly β Rendering
- Loads Pyodide with minimal packages (numpy, basic scientific libraries)
- Python performs data processing, analysis, and computations
- Python returns clean, structured JSON data
- JavaScript receives data and creates Plotly visualizations directly
- Native Plotly.js handles all rendering and interactivity
# Python focuses on computation
import numpy as np
def analyze_data():
# Complex data processing here
x_data = np.random.normal(0, 1, 1000)
y_data = x_data * 2 + np.random.normal(0, 0.5, 1000)
return {
'x': x_data.tolist(),
'y': y_data.tolist(),
'categories': ['A', 'B', 'C'] * 333 + ['A']
}
result = analyze_data()
json.dumps(result) # Clean data structure// JavaScript handles visualization
const data = JSON.parse(pythonResult);
const plotData = [{
x: data.x,
y: data.y,
type: 'scatter',
mode: 'markers'
}];
Plotly.newPlot('chart', plotData, layout);- β Higher reliability: No complex Python Plotly integration
- β Faster loading: Minimal Python dependencies
- β Better performance: Native JavaScript Plotly is optimized for browsers
- β Easier debugging: Clear separation between computation and visualization
- β Flexible: Can easily switch visualization libraries if needed
- β Requires JavaScript knowledge for visualization code
- β Must manually translate between Python data structures and Plotly.js configs
- β Can't leverage some advanced Python Plotly features directly
- Most production use cases - more reliable and maintainable
- Teams comfortable with both Python and JavaScript
- Applications where visualization performance matters
- Projects that might need to switch visualization libraries later
| Aspect | Approach A (Full Python) | Approach B (Hybrid) |
|---|---|---|
| Reliability | β High - Simple, proven pattern | |
| Bundle Size | β Large (~10-15MB total) | β Smaller (~5-8MB total) |
| Startup Time | β Slower (Python Plotly install) | β Faster (minimal packages) |
| Python Complexity | β Simple (familiar Plotly API) | |
| JavaScript Complexity | β Minimal (just rendering) | |
| Performance | β Excellent | |
| Debugging | β Complex (multi-layer) | β Easy (clear boundaries) |
| Flexibility | β Can change viz libraries |
- Your team is Python-heavy with limited JavaScript experience
- You need specific Python Plotly features not easily replicated
- You're prototyping and want familiar Python syntax
- The project is small-scale and reliability isn't critical
- You want maximum reliability and performance
- The project might scale or go to production
- Your team has JavaScript capability
- You value maintainability and debugging simplicity
- You might need to change visualization libraries in the future
- Pyodide + Python Plotly loading order problems
- JSON serialization edge cases
- Version compatibility between Pyodide and Python Plotly
- Difficult to debug when charts don't render
- Designing clean data structures for complex visualizations
- Translating Python analysis results to Plotly.js configurations
- Managing state between Python computations and JavaScript rendering
Both approaches validate the core "El-Cheapo" concept:
- No backend servers required - everything runs client-side
- Single HTML file deployment - can be hosted anywhere
- Sophisticated visualizations - comparable to full-stack solutions
- Cost-effective - no ongoing infrastructure costs
Recommendation: Start with Approach B (Hybrid) for most projects. It offers the best balance of reliability, performance, and maintainability while still achieving the cost-effectiveness goals of the El-Cheapo pattern.
- Download either HTML file
- Open in a modern browser (Chrome, Firefox, Safari, Edge)
- Wait for Pyodide to load (30-60 seconds first time)
- Click buttons to generate visualizations
- Inspect the code to understand the patterns
No installation, no servers, no configuration - just open and run!
While Pyodide offers powerful client-side Python execution, there are scenarios where a purely browser-based Python visualization approach may not be suitable, especially when Python is required for data processing:
- Non-Web-Native GUI Toolkits: If your Python data processing leads to visualizations exclusively rendered by desktop GUI libraries (e.g., some specialized scientific plotting tools built on PyQt, Tkinter, etc.) that lack web-based counterparts or export options, direct browser display is not feasible.
- Extreme Performance or Low-Level Browser API Access: For cutting-edge, highly specialized, or extremely performance-sensitive interactive visualizations that demand direct, low-level manipulation of WebGL/WebGPU contexts or very tight, synchronous loops with browser events, pure JavaScript/TypeScript and WebAssembly (not necessarily Python-in-WebAssembly) might be required for maximum control and minimal overhead.
- Dependencies on Uncompiled Native C Extensions: If your Python data processing relies on specific Python packages with critical C extensions that have not been compiled for WebAssembly and included in Pyodide, that part of your Python code cannot run in the browser, preventing a fully client-side visualization workflow.
- Heavy Server-Side Computation or External Resource Access: Visualizations that inherently require continuous, heavy server-side Python computation (e.g., very long-running, CPU-intensive calculations that would block the browser's UI thread) or constant interaction with external resources (e.g., massive databases, real-time data streams, proprietary APIs) that cannot be accessed directly from the browser (due to security policies like CORS or performance limitations) will necessitate server-side Python processing, with only the processed results sent to the browser for visualization.
Carlos - BlockSecCA