# Figure dashboard with Bonito and WGLMakie

**URL:** https://discourse.julialang.org/t/figure-dashboard-with-bonito-and-wglmakie/133387
**Category:** General Usage
**Tags:** question, plotting, makie, interactivity, observables
**Created:** [October 23, 2025, 1:43pm UTC](https://discourse.julialang.org/t/figure-dashboard-with-bonito-and-wglmakie/133387 "2025-10-23T13:43:17Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![gdestouet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdestouet/32/6175_2.png) [@gdestouet](https://discourse.julialang.org/u/gdestouet)
#### Post date: [October 23, 2025, 1:43pm UTC](https://discourse.julialang.org/t/figure-dashboard-with-bonito-and-wglmakie/133387/1 "2025-10-23T13:43:17Z")

</div>

Hello everyone,

I am currently trying to set up a dashboard of figures using the following code

```julia-auto
using Bonito
using WGLMakie
all_figs = Dict()
function newfig(title)
  fig = Figure()
  if title in keys(all_figs)
    all_figs[title][] = fig
  else
    all_figs[title] = Observable(fig)
  end
  fig
end
fig = newfig("rand");
ax = Axis(fig[1,1])
lines!(ax,randn(10))

App() do
    content = Any[]
    for t in keys(all_figs)
        push!(content,DOM.h2(t))
        push!(content,all_figs[t])
    end
    return Col(content...)
end

fig = newfig("rand");
#sleep(0.01) # <--without it, it fails because of timeout in evaljs_value
ax = Axis(fig[1,1])
lines!(ax,randn(10))

```

Without the small time delay between the creation of the figure and the axis the code fail with the following error:

```julia-auto
ERROR: Timed out
Stacktrace:
  [1] error(s::String)
    @ Base ./error.jl:44
  [2] evaljs_value(session::Session{Bonito.SubConnection}, js::Bonito.JSCode; error_on_closed::Bool, timeout::Int64)
    @ Bonito ~/.julia/packages/Bonito/9fokY/src/session.jl:348
  [3] evaljs_value
    @ ~/.julia/packages/Bonito/9fokY/src/session.jl:294 [inlined]
  [4] insert_plot!(session::Session{Bonito.SubConnection}, scene::Scene, plot::Plot)
    @ WGLMakie ~/.julia/packages/WGLMakie/FbMOf/src/display.jl:469
  [5] insert!(screen::WGLMakie.Screen, scene::Scene, plot::Plot)
    @ WGLMakie ~/.julia/packages/WGLMakie/FbMOf/src/display.jl:480
  [6] push!(scene::Scene, plot::Plot)
    @ Makie ~/.julia/packages/Makie/4JW9B/src/scenes.jl:515
  [7] plot!(scene::Scene, plot::Poly{Tuple{GeometryBasics.HyperRectangle{2, Int64}}})
    @ Makie ~/.julia/packages/Makie/4JW9B/src/interfaces.jl:212
  [8] _create_plot!(F::Function, attributes::Dict{…}, scene::Scene, args::Observable{…})
    @ Makie ~/.julia/packages/Makie/4JW9B/src/figureplotting.jl:411
  [9] poly!(::Scene, ::Vararg{…}; kw::@Kwargs{…})
    @ Makie ~/.julia/packages/Makie/4JW9B/src/recipes.jl:521
 [10] poly!
    @ ~/.julia/packages/Makie/4JW9B/src/recipes.jl:519 [inlined]
 [11] initialize_block!(ax::Axis; palette::Nothing)
    @ Makie ~/.julia/packages/Makie/4JW9B/src/makielayout/blocks/axis.jl:196
 [12] initialize_block!(ax::Axis)
    @ Makie ~/.julia/packages/Makie/4JW9B/src/makielayout/blocks/axis.jl:154
 [13] _block(T::Type{…}, fig_or_scene::Figure, args::Vector{…}, kwdict::Dict{…}, bbox::Nothing; kwdict_complete::Bool)
    @ Makie ~/.julia/packages/Makie/4JW9B/src/makielayout/blocks.jl:405
 [14] _block
    @ ~/.julia/packages/Makie/4JW9B/src/makielayout/blocks.jl:321 [inlined]
 [15] #_block#1568
    @ ~/.julia/packages/Makie/4JW9B/src/makielayout/blocks.jl:266 [inlined]
 [16] _block(::Type{Axis}, ::Figure)
    @ Makie ~/.julia/packages/Makie/4JW9B/src/makielayout/blocks.jl:265
 [17] _block(::Type{Axis}, ::GridPosition; kwargs::@Kwargs{})
    @ Makie ~/.julia/packages/Makie/4JW9B/src/makielayout/blocks.jl:260
 [18] _block
    @ ~/.julia/packages/Makie/4JW9B/src/makielayout/blocks.jl:251 [inlined]
 [19] #_#1566
    @ ~/.julia/packages/Makie/4JW9B/src/makielayout/blocks.jl:242 [inlined]
 [20] Axis(args::GridPosition)
    @ Makie ~/.julia/packages/Makie/4JW9B/src/makielayout/blocks.jl:241
 [21] top-level scope
    @ REPL[528]:1
Some type information was truncated. Use `show(err)` to see complete types.

```

I would like to know if this time delay is necessary or if I am not approaching the problem in the right way.

Thank you in advance !

---

<div class="post-metadata">

### Author: ![sdanisch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sdanisch/32/1406_2.png) [@sdanisch](https://discourse.julialang.org/u/sdanisch)
#### Post date: [October 27, 2025, 6:32pm UTC](https://discourse.julialang.org/t/figure-dashboard-with-bonito-and-wglmakie/133387/2 "2025-10-27T18:32:14Z")

</div>

You’re creating lots of global state.  
That’s not per se bad, but definitely makes it hard to see what’s actually going on in your example.  
Also, displaying a scene that is also inside an app is not a great idea.  
Can you make an MWE (minimal working example) of what you’re trying to do without creating all this global state?

---

<div class="post-metadata">

### Author: ![gdestouet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdestouet/32/6175_2.png) [@gdestouet](https://discourse.julialang.org/u/gdestouet)
#### Post date: [October 28, 2025, 12:39pm UTC](https://discourse.julialang.org/t/figure-dashboard-with-bonito-and-wglmakie/133387/3 "2025-10-28T12:39:43Z")

</div>

Thank you for your answer (and the great work on Bonito and Makie btw !). I have put below a minimal working example:

```julia
using Bonito
using WGLMakie
fig = Figure()
ax = Axis(fig[1, 1])
lines!(ax, randn(1000))
store = Observable(fig)
App() do
    return Col(store)
end
# Update the figure later
fig = Figure()
store[] = fig
ax = Axis(fig[1, 1])
lines!(ax, randn(1000), color = :red)

```

On Firefox I sometimes get timeout errors, but I haven’t been able to reproduce them consistently.

When I am experimenting with a script I want the different figures to be accessible in a dashboard, and then be able to iteratively improve them in the REPL by re-evaluating part of the script (that is why in my initial post I am using a dictionary that store the different figures).

This workflow helps me keep a live dashboard that stays in sync with my script. I prefer that instead of traditional notebook (Pluto, Jupyter, BonitoBook) where you have to scroll through all your notebook to visualize the different figures.

> [@sdanisch](#):
>
> Also, displaying a scene that is also inside an app is not a great idea.

Do you know an alternative way to do that ? Thanks in advance.

---

<div class="post-metadata">

### Author: ![sdanisch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sdanisch/32/1406_2.png) [@sdanisch](https://discourse.julialang.org/u/sdanisch)
#### Post date: [October 28, 2025, 2:37pm UTC](https://discourse.julialang.org/t/figure-dashboard-with-bonito-and-wglmakie/133387/4 "2025-10-28T14:37:05Z")

</div>

Would maybe BonitoBook with auto updates on file change do this for you?  
That’s planned and shouldn’t be too hard to implement.

Otherwise, your main problem is, that putting a figure into the dom of an App, doesn’t block until the plot is fully displayed - display(fig) does that to avoid your issue.  
What you could do is something like this:

```julia
all_figs = Dict()
function get_fig(key)
    fig = all_figs[key] 
    # This should block until the scene is fully displayed
    WGLMakie.get_screen_session(Makie.getscreen(Makie.get_scene(fig)))
   return fig
end

```

It also seems to me like the all\_figs should be an observable, so that the dashboard automatically updates?

---

<div class="post-metadata">

### Author: ![gdestouet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdestouet/32/6175_2.png) [@gdestouet](https://discourse.julialang.org/u/gdestouet)
#### Post date: [November 5, 2025, 1:37pm UTC](https://discourse.julialang.org/t/figure-dashboard-with-bonito-and-wglmakie/133387/5 "2025-11-05T13:37:56Z")

</div>

Thank you for your help !

> [@sdanisch](#):
>
> Would maybe BonitoBook with auto updates on file change do this for you?  
> That’s planned and shouldn’t be too hard to implement.

Not in my case I think, I tend to “save on write” my scripts (e.g. in nvim and helix) when editing and I suspect it will result in unnecessary computations. I would prefer to manually execute parts of it.

> [@sdanisch](#):
>
> What you could do is something like this:
> 
> ```julia-auto
> all_figs = Dict()
> function get_fig(key)
> fig = all_figs[key] 
> # This should block until the scene is fully displayed
> WGLMakie.get_screen_session(Makie.getscreen(Makie.get_scene(fig)))
> return fig
> end
> 
> ```

I tried but `WGLMakie.get_screen_session(Makie.getscreen(Makie.get_scene(fig.val)))` (considering that fig is observable here) is failing as `fig.scene` is `nothing`.

> [@sdanisch](#):
>
> It also seems to me like the all\_figs should be an observable, so that the dashboard automatically updates?

Indeed ! But I didn’t find a solution yet. Also, it might be better to use a `OrderedDict` and update on new insertion.

I was thinking of using a `Scene` as a dashboard but I guess individual plot update will re-render every other plots ?
