Plotting with Plots.jl and Makie
Choose a backend, build a figure from series and attributes, lay out subplots, save reliably, and know when Makie is worth the extra weight.
Backends
| Backend | Strength | Notes |
|---|---|---|
GR | Default, fast, no dependencies beyond a binary | Great for PNG, SVG and PDF on a server |
UnicodePlots | Plots in a terminal | Useful over SSH |
PlotlyJS | Interactive HTML | Needs a browser or a notebook renderer |
PyPlot | Matplotlib output | Requires a Python installation |
PGFPlotsX | Publication LaTeX | Requires a LaTeX toolchain |
using Plots
gr() # chose the backend for this session
default(fmt = :png, size = (800, 500), dpi = 150)
x = range(0, 2pi, length = 200)
p = plot(x, sin.(x);
label = "sin", lw = 2, color = :steelblue,
xlabel = "angle (rad)", ylabel = "value",
title = "Trigonometric functions")
plot!(p, x, cos.(x); label = "cos", ls = :dash, lw = 2)
hline!(p, [0.0]; label = "", color = :grey, alpha = 0.4)
savefig(p, "trig.png")
savefig(p, "trig.svg")plotcreates a figure,plot!adds to the current one. The exclamation mark convention means the call mutates rather than returns something new.- Always pass an explicit label or
label = ""; otherwise the legend fills up with inferred names. - The backend is chosen once per session and cannot be swapped after a plot exists.
Layout and series
p1 = scatter(rand(50), rand(50); title = "scatter", markerstrokewidth = 0)
p2 = histogram(randn(1000); bins = 30, title = "histogram", legend = false)
p3 = bar(["a", "b", "c"], [3, 7, 2]; title = "bar")
grid = plot(p1, p2, p3; layout = (2, 2), size = (900, 700))
savefig(grid, "grid.png")
# a 3D surface
xs = ys = range(-2, 2, length = 50)
p4 = surface(xs, ys, (x, y) -> exp(-(x^2 + y^2)))
savefig(p4, "surface.png")Layouts accept tuples such as (2, 2) or a custom grid with mixed spans through @layout. Build each subplot as a value first, then combine them, rather than chaining everything into one call.
When to use Makie
using CairoMakie # static, no GPU needed
using GLMakie # interactive window or browser
fig = Figure(size = (800, 500))
ax = Axis(fig[1, 1], xlabel = "angle (rad)", ylabel = "value", title = "sin")
lines!(ax, x, sin.(x); color = :steelblue, linewidth = 2)
scatter!(ax, x[1:20:end], sin.(x[1:20:end]); color = :tomato)
fig[2, 1] = Legend(fig, ax, "series")
save("makie.png", fig)💡
On a headless server, Plots with the GR backend and Makie with CairoMakie both render without a display; GLMakie does not. Choose the backend deliberately in a service, or your plotting code will work locally and fail in the container.
FAQ
Plots.jl or Makie?
Plots.jl is quicker to get started and its one API covers many backends. Makie has a more explicit, composable object model, better interactive performance and finer layout control, at the cost of a longer learning curve and heavier dependencies.
Why does savefig produce an empty file?
The figure was never created because a plotting call errored earlier, or a headless session has no display backend. Choose GR or CairoMakie explicitly before the first plotting call.
Related
DataFrames, CSV and tabular data in Julia Debugging, testing and benchmarking Julia code
Last refreshed 2026-09-18.