Best way to use bokeh for a non-interactive simulation visualization

I have a simulation that runs in python and has hundreds of time steps. I am trying to use bokeh to visualize some of the simulation results at each time step in a browser window. The layout of the visualization is fixed, but the underlying data changes at each time step. I don’t want to have any interaction from the browser back to my simulation. I just want a one-way update to the browser to update the plots at the end of each simulation time step. Does anyone have recommendations or example code showing the best way to approach this?

I adapted code from https://bokeh.pydata.org/en/latest/docs/user_guide/server.html#updating-from-threads and came up with the code below (saved as simulation_framework.py) using a dummy simulation update step that just picks random values for x and y. I can run with bokeh serve --show simulation_framework.py but I am still interested if someone has other recommendations for how to do this.

from functools import partial
from random import random
from threading import Thread
import os
import time

from bokeh.models import ColumnDataSource
from bokeh.plotting import curdoc, figure

from tornado import gen

# this must only be modified from a Bokeh session callback
source = ColumnDataSource(data=dict(x=[0], y=[0]))

# This is important! Save curdoc() to make sure all threads
# see the same document.
doc = curdoc()

@gen.coroutine
def update(x, y):
#    source.stream(dict(x=[x], y=[y]))
    source.data = dict(x=[x], y=[y])

def blocking_task():
    num_steps = 100
    for ii in range(num_steps):
        print(ii)
        # do code for simulation time step ii here
        time.sleep(0.1)
        x, y = random(), random()

        # but update the document from callback
        doc.add_next_tick_callback(partial(update, x=x, y=y))
    os._exit(0)

p = figure(x_range=[0, 1], y_range=[0,1])
l = p.circle(x='x', y='y', source=source)

doc.add_root(p)

thread = Thread(target=blocking_task)
thread.start()