Do nonvisible glyph renderers plot lazily?

Hi, I am wondering how glyph rendering works internally as I had a plot with around 5 glyph renderers and just added an extra glyph renderer for each original one to plot the same data but on a different x-axis value. I added a toggle that determined which set of 5 glyph renderers would be visible.

I’ve been having some lag issues, and I’m wondering if this is due to doubling the number of data processing (especially since the plot handles ~20-50 datapoints a minute). So I’m wondering if glyph renderers plot lazily, so when they’re made visible, they go thru the data source then plot and render each point. Or is it the case that they already have each data point plotted and just render it? Because if the renderers have already plotted the points, then perhaps it could cause browser lag even though they aren’t visible?

Here’s an MRE of what I mean:

from random import uniform
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Toggle
from bokeh.plotting import figure, curdoc

ds1 = ColumnDataSource({"timestamp": [], "value1": []})
ds2 = ColumnDataSource({"timestamp": [], "value2": []})

plot = figure(x_axis_type='linear',plot_width=1000, plot_height=600)

line1 = plot.line(x='timestamp', y= 'value1', source=ds1, color='blue')
line2 = plot.line(x='timestamp', y= 'value2', source=ds2, color='red')
line2.visible = False

now = 0

def stream_data():
    global now
    now = now + 1
    new_data1 = {"timestamp": [], "value1": []}
    new_data2 = {"timestamp": [], "value2": []}

    new_data1["timestamp"].append(now)
    new_data1["value1"].append(uniform(0, 5))
    ds1.stream(new_data1)

    new_data2["timestamp"].append(now)
    new_data2["value2"].append(uniform(0, 5))
    ds2.stream(new_data2)

def on_button_click(value):
    if visibility_toggle.active:
        line2.visible = True
        line1.visible = False
    else:
        line1.visible = True
        line2.visible = False

visibility_toggle = Toggle(label="SWITCH GRAPH VIEW")
visibility_toggle.on_click(on_button_click)

curdoc().add_root(column(plot))
curdoc().add_root(visibility_toggle)

update_interval_s = 1
curdoc().add_periodic_callback(stream_data, update_interval_s * 1000)

Please let me know what you think, or if there are better approaches to toggling between a renderers. I originally tried recreating the plot, but that had its own issues and wasn’t operationally exactly what we wanted (having a quick toggle to compare was ideal). Thanks!

Every time there is any re-draw, the entire canvas is always completely re-drawn [1], which means all glyph renderers re-execute their render codepath on any update. Some caveats:

  • If the glyph has set visible to False, then the render path becomes a no-op (immediate return)
  • Some glyphs also use a spatial index to only draw the points that are on screen inside the viewport

I’ve been having some lag issues, and I’m wondering if this is due to doubling the number of data processing (especially since the plot handles ~20-50 datapoints a minute).

Those seem like tiny numbers that should be no problem. We can’t really speculate without a complete Minimal Reproducible Example to actual run and investigate directly.


  1. Some systems can do partial redraws by computing “damage” regions, but this approach adds a huge amount of complexity to a rendering pipeline, as well as its own computational overhead. It’s utilized in various specialized drawing tools, but not usually employed by general plotting tools that I am aware of. ↩︎

1 Like

Thanks for the reply bryan, it would be tricky to produce a complete MRE, so I will just continue digging. But good to know about the glyph renderer internal workings, thanks!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.