Pluto Integration
JSXGraph.jl integrates with Pluto.jl's reactive @bind mechanism, letting you read the live state of a board's interactive elements (draggable points, sliders) directly into Julia variables — and have any downstream cell re-execute automatically when the user drags something in the browser.
Quickstart
Opt in by passing bindable=true to board(...):
using JSXGraph
@bind state board("demo"; xlim=(-5, 5), ylim=(-5, 5), bindable=true) do b
push!(b, point(-2.0, 1.0; name="A"))
push!(b, point( 0.0, -1.0; name="B"))
push!(b, point( 2.0, 2.0; name="C"))
endIn another cell:
xs, ys = points_xy(state) # parallel vectors in declaration order
state["A"].x # access by element nameDrag any point in the rendered board → both cells re-evaluate.
Bound-state shape
When bindable=true, the bound value is a Dict{String,Any}:
| Element kind | Key | Value |
|---|---|---|
point (2D, not fixed) | name attr, else "point_$i" | (x = …, y = …) named tuple |
point3d (not fixed) | name, else "point3d_$i" | (x = …, y = …, z = …) |
slider | name, else "slider_$i" | Float64 value |
Other element kinds (curves, lines, axes, …) do not contribute to the bound state.
Helpers
points_xy(state) -> (xs::Vector{Float64}, ys::Vector{Float64})
points_xyz(state) -> (xs::Vector{Float64}, ys::Vector{Float64}, zs::Vector{Float64})Both helpers project the bound state into parallel coordinate vectors in element-declaration order. points_xy skips 3D points and sliders; points_xyz skips 2D points and sliders.
Duplicate names
A bindable board with two interactive elements sharing the same name raises ArgumentError at construction time — the bound-state schema needs unambiguous keys.
board("dup"; bindable=true) do b
push!(b, point(0.0, 0.0; name="X"))
push!(b, point(1.0, 1.0; name="X")) # → ArgumentError
endNon-bindable boards keep current behaviour (duplicates allowed).
How it works
When bindable=true, the renderer wraps the board's <div> in an outer <span> whose .value property holds the current state. A throttled JavaScript handler (≈30 Hz) updates .value and dispatches a standard input event on every interactive change, with an unconditional final fire on drag-end. Pluto's @bind macro listens for that event — no custom protocol, no extra dependencies on the Julia side.
Outside Pluto (raw HTML page, Documenter, Jupyter), the wrapper is inert: the board renders normally and the input event fires harmlessly with nothing listening.
If AbstractPlutoDingetjes is loaded (which Pluto does automatically), seed coordinates flow through Pluto's binary published_to_js channel instead of being inlined as JSON literals — useful for boards with very large seed arrays. The package extension JSXGraphAbstractPlutoDingetjesExt makes this transparent; the JSON fallback is used everywhere else.
Runnable example
The following Pluto notebook demonstrates a complete spline-editor workflow: drag points in a JSXGraph board, and a Makie plot of the fitted spline recomputes reactively.
Download the notebook — open it in Pluto locally to interact with it.
<!– PLUTONOTEBOOKSTART –>
begin
using Pkg
# Pluto auto-adds `JSXGraph` from the General registry, which (until
# 0.6 is published) currently resolves to a different UUID than the
# local checkout. Remove that auto-added entry (if present), then
# develop the local path. Adjust `path=...` if you place this
# notebook elsewhere.
try
Pkg.rm("JSXGraph")
catch
end
Pkg.develop(PackageSpec(path = joinpath(@__DIR__, "..", "..")))
using JSXGraph
end
JSXGraph.jl + Pluto @bind
This notebook demonstrates JSXGraph.jl's Pluto integration. Drag the points in the board below — the cells below it re-execute reactively with the new coordinates.
The key ingredient is bindable=true on board(...), plus the standard @bind macro to surface the board's interactive state as a Julia variable.
@bind state board("demo"; xlim=(-5, 5), ylim=(-5, 5), bindable=true) do b
push!(b, point(-2.0, 1.0; name="A"))
push!(b, point( 0.0, -1.0; name="B"))
push!(b, point( 2.0, 2.0; name="C"))
end
Raw bound state
state
missing
Parallel coordinate vectors
points_xy(state) projects the bound state into two Vector{Float64}s, in element-declaration order.
xs, ys = points_xy(state)
(Float64[], Float64[])
Live derivation
Any downstream cell that reads state, xs, or ys re-evaluates each time you drag a point.
let
n = length(xs)
sumx = isempty(xs) ? 0.0 : sum(xs)
centroid_x = isempty(xs) ? 0.0 : sumx / n
centroid_y = isempty(ys) ? 0.0 : sum(ys) / n
"centroid of the $(n) draggable points is ($(round(centroid_x; digits=3)), $(round(centroid_y; digits=3)))"
end
"centroid of the 0 draggable points is (0.0, 0.0)"
<!– PLUTONOTEBOOKEND –>
API reference
JSXGraph.points_xy — Function
points_xy(state) -> (xs::Vector{Float64}, ys::Vector{Float64})Project the bound state of a bindable board into parallel x and y coordinate vectors, in the declaration order of the 2D points on the board. 3D points and sliders are skipped.
State entries are recognised as 2D points when they look like a 2-tuple (x, y) or a 2-key dict / NamedTuple (x = …, y = …).
Companion to points_xyz.
JSXGraph.points_xyz — Function
points_xyz(state) -> (xs::Vector{Float64}, ys::Vector{Float64}, zs::Vector{Float64})Project the bound state into parallel x, y, z coordinate vectors, in declaration order of the 3D points on the board. 2D points and sliders are skipped.