Fastest plot backend for Interact + Mux

I am experimenting with Interact.jl for teaching (interactively exploring implications of a macroeconomic model). I also have no clue about web technologies.

The idea is to have model parameters as sliders, solve and simulate the model (\approx 0.1s), and then make plots, about 6–10 simple ones.

I am wondering what the best backend is for this. I experimented with gr(), but perhaps making SVG would be something that would be easier on the browser?

MWE:

using Interact, Mux, Plots
gr()
xgrid = (1:100)/100
ui = @manipulate for α in range(0, 2; step = 0.1)
    plot(xgrid, xgrid.^α)
end
WebIO.webio_serve(page("/", req -> ui), 8001)

Any advice would be appreciated.

using Interact, Mux, GR
xgrid = (1:100)/100
ui = @manipulate for α in range(0, 2; step = 0.1)
    plot(xgrid, xgrid.^α)
end
WebIO.webio_serve(page("/", req -> ui), 8001)

So you could try gr(fmt=:svg) in Plots …

2 Likes

The above code just opens a separate window for the GR plot.

gr(fmt=:svg) works very nicely, thanks!

Also, I wonder how I can show multiple plots in Interact (eg stacked vertically).

Multiple windows are still WIP - sorry.

I wonder if it would be possible to stack plots using Interact.jl, but my CSS-fu is not strong enough for this.

Fortunately your rarely have to touch any html or css with Interact.

I use plotly() for its zoom functionality as I often need it. But I find it runs fast enough and outputs pretty well optimised svgs. I normally plot multiple plots into one plot window instead of as separate plots. I’m not actually sure that its faster now that I think about it, but you would think that it is…

If your model takes 0.1s to run you will want to throttle the sliders so it doesn’t get run too often. Its usually something like throttle(0.1, observe(sli)) but I’m not sure of the syntax for @manipulate as I don’t use the macro. At a certain level of complexity its just easier to use the observables directly.

If you have the time, I would greatly appreciate if you could provide an example with observables, two slides a and b, throttled, and two plots. Eg plots

xgrid = (1:100)/100
plot(xgrid, xgrid.^(a + b))
plot(xgrid, a .+ xgrid .* b) 

I am very new to this and I have a hard time piecing this together.

This one has a (variable) few hundred of each: DEBplant/ui.jl at master · rafaqz/DEBplant · GitHub

But that’s probably hard to follow. So 2 slider 2 plots:

import WebIO, Mux, Interact, Plots
import Plots:px, pct, GridLayout

plotfunction(slider1, slider2) = begin
    plot1 = plot(x -> x*slider1)
    plot2 = plot(x -> x*slider2)
    l = Plots.GridLayout(1, 2)
    l[1, 1] = GridLayout(1, 1, width=0.8pct)
    l[1, 2] = GridLayout(1, 1, width=0.2pct)
    plot(plot1, plot2, layout=l)
end

app(req) = begin
    plt = Observable(plot())
    slider1 = slider(1.0:100.0, label="Sl1")
    slider2 = slider(1.0:100.0, label="Sl2")
    # This is map for observables, its happening continuously
    map!(plotfunction, plt, throttle.(0.1, observe.((slider1, slider2)))...)
    ui = vbox(plt, slider1, slider2)
end

webio_serve(page("/", req -> app(req)), 8000)

Hah the sliders only change the plot axes, but you probably have something more interesting anyway.

3 Likes

I agree that while @manipulate is good for simple cases, it could be better to do things by hand in more complex scenario. An in-between things is widget(x::T) that creates the default widget for an element of type T (for ranges it is a slider) and also the macro Widgets.@auto that allows to create the “default widget” for a variable with as label the name of the variable.

For example:

using Plots, Interact, Mux

dt = 0.2

# Create default widgets with default label
Widgets.@auto params1 = 1:100
Widgets.@auto params2 = 1:100

# Throttled widgets
throttled1 = throttle(dt, params1)
throttled2 = throttle(dt, params2)

# use map to have plots that updates as soon as throttled value updates
plot1 = map(throttled1) do val
    plot(rand(val), rand(val))
end

plot2 = map(throttled2) do val
    plot(rand(val), rand(val))
end

# create layout
ui = vbox(params1, params2, hbox(plot1, plot2))

# serve webapp
WebIO.webio_serve(page("/", req -> ui), 8000)

The layout functions are from CSSUtil.

EDIT: If you want to serve this from a server from multiple users @Raf approach of putting the app in a function is better as otherwise different users from different computers would get the same synced sliders, but if it’s to serve the app locally I think it doesn’t matter

2 Likes

I just remember that @manipulate does support throttle: you can simply write @manipulate throttle=0.2 for i in ...

As above, you can specify the output of the for loop with vbox and hbox

1 Like

Thanks. If I just want to stack (and not necessarily align) many plots vertically, ie just have one plot below another in the order they come in, should I just use vbox?

1 Like

There is also Widgets.div(a, b, c...) which stacks vertically without aligning. See which works better for your use case (for more sophisticated options I’m afraid one would need to write some CSS)

1 Like

You might be interested at looking at the examples using the InspectDR.jl backend, that use Interact.jl + Blink.jl (Web GUIs using Chrome engine, if I am not mistaken):

https://github.com/ma-laforge/InspectDR.jl/tree/master/Blink

Notes:

  • The files under blink/ are mostly just interact/GUI code; the code that generates the plots themselves are placed in a separate file found under ../notebook/ to make things cleaner.
  • You must explicitly add packages Interact, Blink, NumericIO, Colors (and optionally DSP & FFTW) for the examples to work, as described in README.md.
  • I am not just using Mux (I use Blink instead), but I am sure the examples can be displayed in Mux directly with little change in code (since Interact is the main component here).

I suggest checking out example 3_pllstab.jl. It is a relatively complex example, and I think it is somewhat inline with what you might want. Here is a snapshot:

3 Likes

I believe that vbox works to stack plots in Interact. Check the WebIO docs.

1 Like