Basic Donut Chart in Makie.jl (Julia)

The same plot in 14 other libraries — Python: Altair, Bokeh, lets-plot, Matplotlib, Plotly, plotnine, Pygal, Seaborn; R: ggplot2; JavaScript: Chart.js, D3.js, Apache ECharts, Highcharts, MUI X Charts. Compare all 15 side by side: Basic Donut Chart in Python, R, Julia and JavaScript.

A donut chart (ring chart) showing proportions of categorical data as segments of a ring, with a hollow center. Similar to pie charts but the empty center can display summary statistics or additional information. The ring format can be easier to read when comparing segment sizes.

Basic Donut Chart rendered with Makie.jl

Renders

Julia source (Makie.jl)

# anyplot.ai
# donut-basic: Basic Donut Chart
# Library: makie 0.21.9 | Julia 1.11.9
# Quality: 87/100 | Created: 2026-06-25

using CairoMakie
using Colors
using Random

Random.seed!(42)

# Theme tokens
const THEME       = get(ENV, "ANYPLOT_THEME", "light")
const PAGE_BG     = THEME == "light" ? colorant"#FAF8F1" : colorant"#1A1A17"
const ELEVATED_BG = THEME == "light" ? colorant"#FFFDF6" : colorant"#242420"
const INK         = THEME == "light" ? colorant"#1A1A17" : colorant"#F0EFE8"
const INK_SOFT    = THEME == "light" ? colorant"#4A4A44" : colorant"#B8B7B0"
const IMPRINT_PALETTE = [
    colorant"#009E73",  # 1 — brand green
    colorant"#C475FD",  # 2 — lavender
    colorant"#4467A3",  # 3 — blue
    colorant"#BD8233",  # 4 — ochre
    colorant"#AE3030",  # 5 — matte red
    colorant"#2ABCCD",  # 6 — cyan
]

# Data: annual R&D budget allocation by division
const categories = ["Software", "Hardware", "Research", "Testing", "Design", "Support"]
const values     = [34.0, 19.0, 21.0, 10.0, 10.0, 6.0]
const n          = length(categories)
const proportions = values ./ sum(values)

# Segment angles — start from top (−π/2), clockwise
const cum_props    = cumsum(proportions)
const angles_end   = cum_props .* 2π .- π/2
const angles_start = vcat(-π/2, angles_end[1:end-1])

# Figure — square canvas → 2400×2400 px (size × px_per_unit = 1200 × 2)
fig = Figure(
    size            = (1200, 1200),
    fontsize        = 14,
    backgroundcolor = PAGE_BG,
)

ax = Axis(
    fig[1, 1];
    title              = "R&D Budget Allocation · donut-basic · julia · makie · anyplot.ai",
    titlesize          = 16,
    titlecolor         = INK,
    titlegap           = 10,
    backgroundcolor    = PAGE_BG,
    aspect             = DataAspect(),
)
hidespines!(ax)
hidedecorations!(ax)
limits!(ax, -1.35, 1.35, -1.35, 1.35)

# Donut ring geometry
const OUTER_R = 1.0
const INNER_R = 0.50
const N_ARC   = 220

# Draw annular segments as polygons
for i in 1:n
    θs = range(angles_start[i], angles_end[i]; length=N_ARC)
    ox = OUTER_R .* cos.(θs)
    oy = OUTER_R .* sin.(θs)
    ix = INNER_R .* cos.(reverse(θs))
    iy = INNER_R .* sin.(reverse(θs))
    pts = Point2f.(vcat(ox, ix), vcat(oy, iy))
    poly!(ax, pts; color=IMPRINT_PALETTE[i], strokecolor=PAGE_BG, strokewidth=3.0)
end

# Percentage labels inside segments (skip very small slices)
const dominant_i = argmax(proportions)
for i in 1:n
    proportions[i] < 0.07 && continue
    θ_mid = (angles_start[i] + angles_end[i]) / 2
    r_mid = (OUTER_R + INNER_R) / 2
    pct   = round(Int, proportions[i] * 100)
    is_dominant = (i == dominant_i)
    text!(ax, "$(pct)%";
          position = Point2f(r_mid * cos(θ_mid), r_mid * sin(θ_mid)),
          align    = (:center, :center),
          fontsize = is_dominant ? 20 : 16,
          font     = is_dominant ? :bold : :regular,
          color    = RGBf(1.0, 1.0, 1.0))
end

# Center annotation — total budget and label
text!(ax, "\$4.8M";
      position = Point2f(0.0, 0.12),
      align    = (:center, :center),
      fontsize = 24,
      color    = INK)
text!(ax, "Total Budget";
      position = Point2f(0.0, -0.14),
      align    = (:center, :center),
      fontsize = 13,
      color    = INK_SOFT)

# Legend
elems = [PolyElement(color=IMPRINT_PALETTE[i], strokecolor=:transparent, strokewidth=0)
         for i in 1:n]
Legend(fig[1, 2], elems, categories;
       labelsize       = 14.0f0,
       labelcolor      = INK,
       framevisible    = false,
       framecolor      = INK_SOFT,
       backgroundcolor = ELEVATED_BG,
       patchsize       = (20.0f0, 16.0f0),
       rowgap          = 10,
       padding         = (12, 12, 12, 12))

colsize!(fig.layout, 1, Relative(0.72))
colgap!(fig.layout, 8)

save("plot-$(THEME).png", fig; px_per_unit=2)

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/donut-basic/makie/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "donut-basic",
  "language": "julia",
  "library": "makie",
  "page": "https://anyplot.ai/donut-basic/julia/makie",
  "hub": "https://anyplot.ai/donut-basic",
  "code_json": "https://api.anyplot.ai/specs/donut-basic/makie/code",
  "spec_json": "https://api.anyplot.ai/specs/donut-basic",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/donut-basic/julia/makie/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/donut-basic/julia/makie/plot-dark.png",
  "quality_score": 87.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Basic Donut Chart on anyplot.ai.

Other implementations