How Bokeh Maintains Bidirectional Communication
The Bokeh visualization library achieves real-time, bidirectional synchronization between a Python environment and a client web browser primarily through the Bokeh Server architecture. By pairing a client-side JavaScript rendering engine (BokehJS) with a persistent Python backend over WebSockets, Bokeh continuously reflects Python data changes in the browser and transmits user-driven browser events back to Python callbacks.
The Client-Server Architecture
Bokeh splits the visualization workload between two environments:
- BokehJS (Client): A JavaScript library that runs inside the user's browser, responsible for rendering interactive canvas elements, handling UI events, and maintaining a local copy of visualization data.
- Bokeh Server (Backend): A Python process built on the Tornado web framework that executes application logic, computes data transformations, and processes interactions.
The Shared Document Model
At the heart of Bokeh's synchronization mechanism is the
Document object. A Document acts as a central
data structure containing all the components of a plot—such as axes,
glyphs, layouts, and data sources—represented as declarative models.
Every model in Python has a precise counterpart in BokehJS. For
example, a Python ColumnDataSource has an identical
JavaScript ColumnDataSource model in BokehJS. When an
application initializes, the server serializes the entire
Document into a JSON representation and sends it to
BokehJS, establishing identical state on both sides.
The WebSocket Protocol
Once the initial render occurs, Bokeh abandons standard stateless HTTP polling and establishes a persistent, full-duplex WebSocket connection between the browser and the Tornado server.
Bokeh employs a specialized protocol over this WebSocket connection that transmits lightweight differential updates (patches) rather than resending entire plots. This protocol supports both text (JSON) and binary formats, enabling low-overhead transfer of large numeric arrays (such as NumPy buffers) directly into JavaScript typed arrays.
Two-Way Event Synchronization
The WebSocket connection powers bidirectional data flow in two distinct directions:
1. Python to Browser Updates
When Python code modifies a property of a plot element—such as
updating the coordinates in a ColumnDataSource or altering
a title—the Python Document automatically captures the
change.
- The server creates a patch message containing the unique ID of the modified model, the specific attribute changed, and the new value.
- This patch is sent over the WebSocket to the browser.
- BokehJS receives the message, applies the change to its local model, and triggers an efficient canvas re-render for only the affected component.
2. Browser to Python Updates
When a user interacts with a visualization (e.g., dragging a slider, selecting points with a lasso tool, or clicking a button), BokehJS captures the UI event.
- BokehJS updates its local model properties and packages the delta into an update message.
- The message is sent across the WebSocket to the Bokeh Server.
- The Python server updates its local
Documentto match the browser's state and automatically executes any Python callback functions (such as@on_change) registered to that specific event or property.
Through this persistent loop of synchronized Document models and minimal patch messaging over WebSockets, Bokeh maintains a responsive, low-latency bridge between Python computational power and interactive browser interfaces.