Pyplet couples a Tornado backend with Pyodide-powered clients so you can write full-stack apps in pure Python. The micro apps in this repository showcase how Pyplet bridges browser DOM APIs, plotting libraries, and WebSockets to build interactive experiences quickly.
Each example ships as a pair of <name>_client.py and <name>_server.py files plus a shared config.py. The client code runs in Pyodide, the server in Tornado, and the packaging hooks allow Pyplet to bundle, serve, and bootstrap everything dynamically.
To run these apps you need Pyplet installed in your environment (for example from the main Pyplet repository or a future package distribution). Point Pyplet’s apps root to this folder and start the server:
# From a project that has Pyplet installed
PYPLET_APPS=/path/to/pyplet_examples pyplet startIf you vendor this repository inside another project (for instance under apps/pyplet_examples), you can use a relative apps root:
PYPLET_APPS=apps/pyplet_examples pyplet startThese examples are independent of any particular “template” project: you can wire them into any Pyplet-powered application by configuring the apps root appropriately.
- Real-time multi-user chat driven entirely by async WebSocket loops.
- Demonstrates server-side user pooling and broadcast messaging plus DOM updates directly from Python.
chat_client.py:26decodes the initial handshake so the UI can announce the assigned username.chat_client.py:31wraps the DOM change event withcreate_proxyto send WebSocket messages straight from Python.chat_server.py:35repackages incoming text andchat_server.py:42fan-outs the payload to every connected peer.
- Live monitoring panel that charts connection counts from other example services.
- Highlights streaming JSON over WebSockets into Matplotlib figures rendered in the browser.
dashboard_client.py:17draws historical series into Matplotlib lines before the figure is mounted inside the DOM.dashboard_client.py:28streams live JSON updates, mutating line data and rescaling the axes (dashboard_client.py:32).dashboard_server.py:23samples connection counts from other services, whiledashboard_server.py:45keeps a background task pushing updates.
- Simulates a desktop environment with detachable windows loading the main site.
- Mixes
pyplet.domprimitives with jQuery UI dialogs to show interop with existing JS widgets. desktop_client.py:8builds a dialog with an iframe mounted viapyplet.dom, reproducing desktop-like windows.desktop_client.py:27renders the Pyplet DOM tree into the browser and wireson_loadfor each popup.desktop_client.py:31hands control to jQuery UI dialogs, anddesktop_client.py:44strips navigation chrome from the embedded page.
- Fully client-side demo that leverages NumPy and Matplotlib via Pyodide.
- Shows how to package additional libraries with
client_librarieswithout needing a Python server loop. frontend_only_client.py:6composes the page entirely withpyplet.domfrom the browser context.frontend_only_client.py:12andfrontend_only_client.py:14use NumPy plus Matplotlib in Pyodide to plot locally.frontend_only_server.py:1declares the extra client libraries so the bundle ships scientific tooling without a Python backend.
- Streams random matrices from the server and draws them as heatmaps.
- Illustrates binary data transfer over WebSockets and live figure updates.
numpy_client.py:12deserializesnp.loadbuffers from WebSocket frames andnumpy_client.py:14seeds a liveimshow.numpy_client.py:18updates the image in-place for smooth animation as data arrives.numpy_server.py:15creates random matrices each second andnumpy_server.py:18streams the raw bytes to clients.
- Broadcast version of the NumPy stream where every viewer shares the same feed.
- Uses a background task to push frames to all connected clients, demonstrating fan-out patterns.
sync_numpy_client.py:1reuses the regular NumPy client loop to stay in sync with the shared feed.sync_numpy_server.py:17launches a background task that continually generates frames for all viewers.sync_numpy_server.py:24iterates active sockets safely, removing disconnected clients during broadcast.
- Mirrors a jQuery UI slider across every connected browser through a shared WebSocket loop.
- Highlights how to wrap DOM callbacks with
asyncio.create_taskwithout blocking Pyodide. sync_slider_client.py:24forwards slider moves asynchronously viacreate_proxy_with_this.sync_slider_server.py:32handlesclosing_messageto drop peers, whilesync_slider_server.py:36fans out updates withasyncio.gather.
Gradient Descent Playground (gradient_descent_playground_client.py, gradient_descent_playground_server.py)
A tiny 1D optimization playground that runs entirely in the browser via Pyodide. It demonstrates how to pair rich canvas rendering, interactive controls, and optimizer implementations within a Pyplet micro app.
gradient_descent_playground_client.py— browser logic (UI, state, animation, rendering, events)gradient_descent_playground_server.py— minimal server glue (route for the app)
The client runs in Pyodide. DOM is built with pyplet.dom and events are wired through Pyodide proxies so JavaScript can call back into Python.
- Pyplet loads the module in the browser and discovers
client_init(). client_init()constructsGradientDescentPlaygroundAppand awaitssetup().setup()builds the DOM, registers event proxies, samples the objective, and initializes state.- When the page unloads,
cleanup()removes listeners and destroys proxies.
See gradient_descent_playground_client.py:1160 (client_init) and gradient_descent_playground_client.py:1136 (cleanup).
- Function: choose an objective from the sidebar
- Optimizer:
GD,Momentum,Adam - Hyperparameters: sliders for
lr,beta,beta1,beta2,eps - Transport:
Play,Pause,Step,Reset - Canvas interactions:
- Click sets the current x position
- Mouse wheel zooms
- Middle-button drag pans
- Diagnostics readout removed so the sidebar now focuses entirely on the controls described above.
- Layout & wiring:
render_layout,setup_canvas,setup_controls(seegradient_descent_playground_client.py) - State: hyperparameters + iterate,
reset_state,resample,evaluate_state - Animation:
play,pause,step_once,on_animation_frame - Rendering:
draw+ helpers (draw_grid,draw_axes, coordinate transforms)
Add a new objective function:
- Create a new entry in the
FUNCTIONSdictionary with a uniquekeyand label (gradient_descent_playground_client.py:92). - Provide a radio option in the layout if you want it selectable (
gradient_descent_playground_client.py:243).
Add a new optimizer:
- Implement its update rule in
apply_optimizer_step(...)(gradient_descent_playground_client.py:974). - Initialize/reset accumulators in
reset_optimizer_state(...)(gradient_descent_playground_client.py:654). - Add a radio button to the Optimizer group in
render_layout(...)(gradient_descent_playground_client.py:255). - Add slider(s) in
setup_controls(...)and updateon_hyperparameter_change(...)(gradient_descent_playground_client.py:362,gradient_descent_playground_client.py:523).
- Autograd NumPy is imported as
anp. Use it for differentiable math. - Keep app-specific assets in this folder for packaging simplicity.
- If you tweak
domain,gradient_clip, or convergence thresholds, the canvas auto-scales unless the user has panned/zoomed.
No automated test suite yet. Run the server, interact with the UI, and watch for console errors. Confirm sliders, optimizer selection, and canvas rendering work as expected.
- Nothing renders: ensure the server route is correct and the app URL matches the folder path.
- Events not firing: check proxies are created and not garbage-collected; see
setup_controlsandsetup_canvasingradient_descent_playground_client.py.
These samples are meant to be launched through Pyplet’s packaging machinery (config.py). Start from any of them to explore how Pyplet keeps Python code running fluidly on both sides of the wire.