Webpage with reactive plot animation

I’d like to have a webpage with a plot which gets updated automatically every second, and which depends on some user input. Schematically, something like:

julia> xs = [0]; vx = ReactiveSlider(initial_value=1)
       while true
           push!(xs, xs[end]+vx[])  
           println(x)
           sleep(1)
           plot!(xs)   # update plot
       end

Or, for a concrete example, “Here is a plot of how much distance the car has traveled so far (x). Adjust the slider value to adjust the speed of the car (vx) in real time”

I was able to get this working in Pluto. But as far as I know, one can still not easily serve a (dynamic, julia-process-backed) notebook as a webpage that multiple people can access simultaneously, right? What would be my best option for this? Dash? Genie? Dash being stateless makes it sound messy (from my limited experience).

Did you have a look at Bonito.jl and Oxygen.jl?

Thank you for the recommendation! I got something working by tweaking a Bonito.jl example:

using Bonito, Bonito.Observables, Base.Threads

import Bonito.TailwindDashboard as D

function create_svg(sl_nsamples, sl_sample_step, sl_phase, sl_radii, color)
    width, height = 900, 300
    cxs_unscaled = [i*sl_sample_step + sl_phase for i in 1:sl_nsamples]
    cys = sin.(cxs_unscaled) .* height/3 .+ height/2
    cxs = cxs_unscaled .* width/4pi
    rr = sl_radii
    # DOM.div/svg/etc is just a convenience in Bonito for using Hyperscript, but circle isn't wrapped like that yet
    geom = [SVG.circle(cx=cxs[i], cy=cys[i], r=rr, fill=color(i)) for i in 1:sl_nsamples[]]
    return SVG.svg(SVG.g(geom...);
        width=width, height=height
    )
end

obs_phase = Observable(1.0)

app = App() do session
    colors = ["black", "gray", "silver", "maroon", "red", "olive", "yellow", "green", "lime", "teal", "aqua", "navy", "blue", "purple", "fuchsia"]
    color(i) = colors[i%length(colors)+1]
    sl_nsamples = D.Slider("nsamples", 1:200, value=100)
    sl_sample_step = D.Slider("sample step", 0.01:0.01:1.0, value=0.1)
    sl_phase = D.Slider("phase", 0.0:0.1:6.0, value=0.0)
    sl_radii = D.Slider("radii", 0.1:0.1:60, value=10.0)
    svg = map(create_svg, sl_nsamples.value, sl_sample_step.value, obs_phase, sl_radii.value, color)
    return DOM.div(D.FlexRow(D.FlexCol(sl_nsamples, sl_sample_step, obs_phase, sl_radii), svg))
end

@spawn for v in -10:0.01:10
    obs_phase[] = v
    sleep(0.01)
end

app

It’s quite straight-forward and it looks cool, props to @sdanisch !

Not quite sure how to make that logic work out per-user (maybe just moving the spawn and observable inside of the do), but I’ll figure it out.

I’ve made a couple of interactive pages using what I think is a pretty general solution. It depends on HTTP.jl for communication and no web frameworks. Here’s the second one, that I just completed: 2D Vortex Dynamics.

You mentioned automatic updates of a plot, so my approach might be useful for you, because I use websockets, which let you continuously stream data from the Julia process into the page.

Another solution is the plutosliderserver.