A tutorial on how to add interactive Makie.jl plots to your blog
Wouldn’t it be cool to have 3D interactive visualizations right in your blog posts?
Instead of just showing static images, you could let readers play around with interactive plots—maybe to get a better handle on the data, or just for the fun of it. Since I didn’t want to depend on any backend services I am using static HTML files to host the interactive plots.
I figured I couldn’t be the first to think of this, and I wasn’t wrong. I found Aaron’s blog post, which walks you through creating interactive plots on a web page using WGLMakie.jl—the web-based backend for the Makie.jl plotting library.
Aaron’s method is now about four years old, and sadly it doesn’t work anymore. I was determined to make it happen, so I reached out for help and, as usual, the Julia community came through. Simon Danisch, the creator of Makie.jl, shared some valuable tips on setting everything up and this blog post is the result.
Note
Besides WGLMakie.jl we will also need Bonito.jl to create the HTML descriptions, which will enable us to embed the plot in a blog post.
First, we need a location to store the script that generates our plots. I prefer to group all files for a specific post in a single folder. For this post, I created a folder called _posts/guides/interactive-blog/
and saved the script as plots.jl
. As a preliminary example we will just plot some random data.
using Bonito, WGLMakie
output_folder = "_posts/guides/interactive-blog/"
function as_html(io, session, app)
dom = Bonito.session_dom(session, app)
show(io, MIME"text/html"(), Bonito.Pretty(dom))
end
session = Session(NoConnection(); asset_server=NoServer())
And the code that generates the plot:
open(output_folder * "scatter.html", "w") do io
println(io, """<center>""")
app = App() do
markersize = Bonito.Slider(range(0.01, stop=0.11, length=6), value=0.07)
scale_value = DOM.div("\\(s = \\)", markersize.value)
# Create a scatter plot
fig, ax = meshscatter(rand(3, 100), markersize=markersize, figure=(; size=(500, 500)))
# Return the plot and the slider
return Bonito.record_states(session, DOM.div(fig, scale_value, markersize))
end;
as_html(io, session, app)
println(io, """</center>""")
end
The script above creates a scatter plot with random data and saves it as scatter.html
. Which we can then include in our blog post using the following liquid:
{% include_relative scatter.html %}
Warning
The HTML files created by `record_states` may be large, since it needs to record all combinations of widget states. This could make your website less responsive. See the documentation for more information.
This will render the scatter plot where the liquid tag is placed:
Cool right? Try moving the slider to change the size of the markers. You can also click and drag to rotate the plot.
Warning
We have to pay special attention to the `session` object. The first session will include the setup for any session that’s included afterwards. You’ll also need to follow the order of rendering, since new dependencies get included in the session that first “sees” that dependency.
To render multiple plots we need to use a Subsession
, which skips uploading similar assets/data and setup. The sub session can be created by calling Session(session)
with the parent session as an argument.
open(output_folder * "volume.html", "w") do io
println(io, """<center>""")
sub = Session(session)
app = App(volume(rand(10, 10, 10), figure=(; size=(500, 500))))
as_html(io, sub, app)
println(io, """</center>""")
end
Let’s plot a differential equation using the DifferentialEquations.jl package. We will solve the Lorenz system of differential equations and plot the result.
using DifferentialEquations
function lorenz!(du, u, p, t)
du[1] = 10.0 * (u[2] - u[1])
du[2] = u[1] * (28.0 - u[3]) - u[2]
du[3] = u[1] * u[2] - (8 / 3) * u[3]
end
u0 = [1.0; 0.0; 0.0]
tspan = (0.0, 100.0)
open(output_folder * "diffeq.html", "w") do io
sub = Session(session)
println(io, """<center>""")
app = App() do
prob = ODEProblem(lorenz!, u0, tspan)
sol = solve(prob)
# sample Point3f data
data = [Point3f(sol(t)) for t in range(0, stop=100, length=10000)]
# plot using lines with colormap
fig = Figure(size=(500, 500))
lines(fig[1, 1], data, color = 1:10000, colormap = :plasma, transparency=true)
fig
end
as_html(io, sub, app)
println(io, """</center>""")
end
We have seen how to create interactive plots using WGLMakie.jl and Bonito.jl. This is a great way to engage your readers and make your blog posts more interactive. I hope you found this tutorial useful and that you will start adding interactive plots to your blog!
Lange, Stefan de (Feb 2025). Creating Interactive Blog Posts with WGLMakie.jl. Hi!. https://langestefan.github.io.
@article{lange2025creating-interactive-blog-posts-with-wglmakie-jl,
title = {Creating Interactive Blog Posts with WGLMakie.jl},
author = {Lange, Stefan de},journal = {Hi!},
year = {2025},
month = {Feb},
url = {https://langestefan.github.io/blog/2025/interactive-julia-plotting/}
}