Feynman Diagram for Particle Interactions — Makie.jl

A Feynman diagram visualizes interactions between subatomic particles in quantum field theory. Different line styles represent different particle types: straight lines for fermions (electrons, quarks), wavy lines for photons, curly/looped lines for gluons, and dashed lines for scalar bosons (e.g., Higgs). Lines meet at vertices representing interaction points. Invented by Richard Feynman, these diagrams are both a computational tool and a cultural icon of modern physics.

Feynman Diagram for Particle Interactions rendered with Makie.jl

Renders

Julia source (Makie.jl)

# anyplot.ai
# feynman-basic: Feynman Diagram for Particle Interactions
# Library: makie 0.22.10 | Julia 1.11.9
# Quality: 93/100 | Created: 2026-06-03

using CairoMakie
using Colors
using Random

Random.seed!(42)

const THEME     = get(ENV, "ANYPLOT_THEME", "light")
const PAGE_BG   = THEME == "light" ? colorant"#FAF8F1" : colorant"#1A1A17"
const PANEL_BG  = THEME == "light" ? colorant"#F2F0E9" : colorant"#222220"
const INK       = THEME == "light" ? colorant"#1A1A17" : colorant"#F0EFE8"
const INK_SOFT  = THEME == "light" ? colorant"#4A4A44" : colorant"#B8B7B0"
const INK_MUTED = THEME == "light" ? colorant"#6B6A63" : colorant"#A8A79F"

const FERMION_COLOR = colorant"#009E73"  # brand green  (e-, e+, μ-, μ+, quarks)
const GLUON_COLOR   = colorant"#C475FD"  # lavender     (gluons / QCD)
const PHOTON_COLOR  = colorant"#4467A3"  # blue         (photons / EM bosons)
const SCALAR_COLOR  = colorant"#BD8233"  # ochre        (scalar bosons / Higgs)

# ─── Makie @recipe: composable Feynman propagator plot types ─────────────────
# Each recipe becomes a reusable, composable CairoMakie primitive — a Makie-
# exclusive feature that makes the diagram grammar extensible and domain-specific.

# Fermion propagator: solid line with a midpoint direction arrow
@recipe(FermionProp, xs, ys, direction) do scene
    Attributes(
        color     = FERMION_COLOR,
        linewidth = 2.5,
        arrowsize = 14,
    )
end

function Makie.plot!(fp::FermionProp)
    xs        = fp[1][]
    ys        = fp[2][]
    direction = fp[3][]   # 1 = particle (forward), -1 = antiparticle (backward)
    clr       = fp.color[]
    lw        = fp.linewidth[]
    arrsz     = fp.arrowsize[]

    lines!(fp, xs, ys; color = clr, linewidth = lw)

    n   = length(xs)
    mid = n ÷ 2
    dx  = xs[min(mid + 1, n)] - xs[max(mid - 1, 1)]
    dy  = ys[min(mid + 1, n)] - ys[max(mid - 1, 1)]
    len = sqrt(dx^2 + dy^2)
    if len > 1e-9
        arrows!(fp, [xs[mid]], [ys[mid]],
                [direction * 0.001 * dx / len],
                [direction * 0.001 * dy / len];
                arrowsize = arrsz, color = clr, linewidth = 0)
    end
    fp
end

# Photon propagator: transverse sinusoidal wave
@recipe(PhotonProp, x1, y1, x2, y2) do scene
    Attributes(
        color     = PHOTON_COLOR,
        linewidth = 2.5,
        n_waves   = 9,
        amplitude = 0.038,
    )
end

function Makie.plot!(pp::PhotonProp)
    x1, y1 = pp[1][], pp[2][]
    x2, y2 = pp[3][], pp[4][]
    clr     = pp.color[]
    lw      = pp.linewidth[]
    nw      = pp.n_waves[]
    amp     = pp.amplitude[]

    dx  = x2 - x1; dy = y2 - y1
    len = sqrt(dx^2 + dy^2)
    nx  = -dy / len; ny = dx / len   # unit normal (transverse direction)

    t    = LinRange(0.0, 1.0, 600)
    xs_w = @. x1 + t * dx + amp * sin(nw * 2π * t) * nx
    ys_w = @. y1 + t * dy + amp * sin(nw * 2π * t) * ny
    lines!(pp, xs_w, ys_w; color = clr, linewidth = lw)
    pp
end

# Gluon propagator: one-sided curly bumps via |sin| — visually distinct from photon
@recipe(GluonProp, x1, y1, x2, y2) do scene
    Attributes(
        color     = GLUON_COLOR,
        linewidth = 2.5,
        n_loops   = 7,
        amplitude = 0.048,
    )
end

function Makie.plot!(gp::GluonProp)
    x1, y1 = gp[1][], gp[2][]
    x2, y2 = gp[3][], gp[4][]
    clr     = gp.color[]
    lw      = gp.linewidth[]
    nl      = gp.n_loops[]
    amp     = gp.amplitude[]

    dx  = x2 - x1; dy = y2 - y1
    len = sqrt(dx^2 + dy^2)
    nx  = -dy / len; ny = dx / len

    t    = LinRange(0.0, 1.0, 800)
    xs_c = @. x1 + t * dx + amp * abs(sin(nl * π * t)) * nx
    ys_c = @. y1 + t * dy + amp * abs(sin(nl * π * t)) * ny
    lines!(gp, xs_c, ys_c; color = clr, linewidth = lw)
    gp
end

# Scalar boson propagator: dashed line (Higgs, etc.)
@recipe(ScalarProp, x1, y1, x2, y2) do scene
    Attributes(
        color     = SCALAR_COLOR,
        linewidth = 2.5,
        n_dashes  = 12,
    )
end

function Makie.plot!(sp::ScalarProp)
    x1, y1 = sp[1][], sp[2][]
    x2, y2 = sp[3][], sp[4][]
    clr     = sp.color[]
    lw      = sp.linewidth[]
    nd      = sp.n_dashes[]

    xs_d = Float64[]
    ys_d = Float64[]
    for i in 0:(nd - 1)
        t0 = i / nd
        t1 = (i + 0.55) / nd
        push!(xs_d, x1 + t0 * (x2 - x1), x1 + t1 * (x2 - x1), NaN)
        push!(ys_d, y1 + t0 * (y2 - y1), y1 + t1 * (y2 - y1), NaN)
    end
    lines!(sp, xs_d, ys_d; color = clr, linewidth = lw)
    sp
end

# ─── Figure: two-panel GridLayout ────────────────────────────────────────────
fig = Figure(
    size            = (1600, 900),
    fontsize        = 14,
    backgroundcolor = PAGE_BG,
)

# Title spans both columns
Label(fig[0, 1:2], "feynman-basic · julia · makie · anyplot.ai";
      fontsize = 20, color = INK, halign = :center)

# ─── Left panel: QED e+e- → μ+μ- s-channel diagram ──────────────────────────
ax = Axis(fig[1, 1];
    backgroundcolor    = PAGE_BG,
    topspinevisible    = false,
    rightspinevisible  = false,
    leftspinevisible   = false,
    bottomspinevisible = false,
    xgridvisible       = false,
    ygridvisible       = false,
    xticksvisible      = false,
    yticksvisible      = false,
    xticklabelsvisible = false,
    yticklabelsvisible = false,
)
xlims!(ax, -0.06, 1.06)
ylims!(ax, 0.0, 1.0)

text!(ax, 0.50, 0.96;
      text = "e⁺e⁻ → μ⁺μ⁻  (QED s-channel)",
      fontsize = 16, color = INK_SOFT, align = (:center, :top))

# Vertex positions
v1x, v1y = 0.30, 0.50
v2x, v2y = 0.70, 0.50

# External leg endpoints
em_x0, em_y0   = 0.02, 0.78   # e-  incoming top-left
ep_x0, ep_y0   = 0.02, 0.22   # e+  incoming bottom-left
mum_x1, mum_y1 = 0.98, 0.78   # μ-  outgoing top-right
mup_x1, mup_y1 = 0.98, 0.22   # μ+  outgoing bottom-right

n_pts = 100
fermionprop!(ax, LinRange(em_x0, v1x, n_pts), LinRange(em_y0, v1y, n_pts), 1)
fermionprop!(ax, LinRange(ep_x0, v1x, n_pts), LinRange(ep_y0, v1y, n_pts), -1)
fermionprop!(ax, LinRange(v2x, mum_x1, n_pts), LinRange(v2y, mum_y1, n_pts), 1)
fermionprop!(ax, LinRange(v2x, mup_x1, n_pts), LinRange(v2y, mup_y1, n_pts), -1)

photonprop!(ax, v1x, v1y, v2x, v2y)

scatter!(ax, [v1x, v2x], [v1y, v2y]; color = INK, markersize = 14, strokewidth = 0)

text!(ax, em_x0 - 0.01, em_y0 + 0.04;
      text = "e⁻", fontsize = 20, color = FERMION_COLOR, align = (:right, :bottom))
text!(ax, ep_x0 - 0.01, ep_y0 - 0.04;
      text = "e⁺", fontsize = 20, color = FERMION_COLOR, align = (:right, :top))
text!(ax, mum_x1 + 0.01, mum_y1 + 0.04;
      text = "μ⁻", fontsize = 20, color = FERMION_COLOR, align = (:left, :bottom))
text!(ax, mup_x1 + 0.01, mup_y1 - 0.04;
      text = "μ⁺", fontsize = 20, color = FERMION_COLOR, align = (:left, :top))
text!(ax, (v1x + v2x) / 2, v1y + 0.10;
      text = "γ* (virtual photon)", fontsize = 17, color = PHOTON_COLOR,
      align = (:center, :bottom))

arrows!(ax, [0.06], [0.07], [0.13], [0.0];
        arrowsize = 10, color = INK_MUTED, linewidth = 1.5)
text!(ax, 0.125, 0.035;
      text = "time →", fontsize = 15, color = INK_MUTED, align = (:center, :top))

# ─── Right panel: Propagator type reference ───────────────────────────────────
ax2 = Axis(fig[1, 2];
    backgroundcolor    = PANEL_BG,
    topspinevisible    = false,
    rightspinevisible  = false,
    leftspinevisible   = false,
    bottomspinevisible = false,
    xgridvisible       = false,
    ygridvisible       = false,
    xticksvisible      = false,
    yticksvisible      = false,
    xticklabelsvisible = false,
    yticklabelsvisible = false,
)
xlims!(ax2, 0.0, 1.0)
ylims!(ax2, 0.0, 1.0)

text!(ax2, 0.50, 0.95;
      text = "Propagator Types", fontsize = 16, color = INK_SOFT, align = (:center, :top))

prop_ys     = [0.73, 0.53, 0.33, 0.13]
prop_labels = ["Fermion  (e⁻, μ, q)", "Photon  (γ)", "Gluon  (g)", "Scalar  (H)"]
prop_colors = [FERMION_COLOR, PHOTON_COLOR, GLUON_COLOR, SCALAR_COLOR]

fermionprop!(ax2, LinRange(0.06, 0.52, n_pts), fill(prop_ys[1], n_pts), 1;
             color = FERMION_COLOR, arrowsize = 12)
photonprop!(ax2, 0.06, prop_ys[2], 0.52, prop_ys[2];
            color = PHOTON_COLOR, n_waves = 7, amplitude = 0.035)
gluonprop!(ax2, 0.06, prop_ys[3], 0.52, prop_ys[3];
           color = GLUON_COLOR, n_loops = 5, amplitude = 0.04)
scalarprop!(ax2, 0.06, prop_ys[4], 0.52, prop_ys[4];
            color = SCALAR_COLOR, n_dashes = 10)

for (y, lbl, clr) in zip(prop_ys, prop_labels, prop_colors)
    text!(ax2, 0.56, y; text = lbl, fontsize = 13, color = clr, align = (:left, :center))
end

# Column widths: 65% main diagram, 35% reference panel
colsize!(fig.layout, 1, Relative(0.65))
colsize!(fig.layout, 2, Relative(0.35))

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/feynman-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": "feynman-basic",
  "language": "julia",
  "library": "makie",
  "page": "https://anyplot.ai/feynman-basic/julia/makie",
  "hub": "https://anyplot.ai/feynman-basic",
  "code_json": "https://api.anyplot.ai/specs/feynman-basic/makie/code",
  "spec_json": "https://api.anyplot.ai/specs/feynman-basic",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/feynman-basic/julia/makie/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/feynman-basic/julia/makie/plot-dark.png",
  "quality_score": 93.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Feynman Diagram for Particle Interactions on anyplot.ai.

Other implementations