Spiral Time Series Chart — Makie.jl

A time series plotted along a spiral (Archimedean or logarithmic), where each full revolution represents one cycle period (e.g., one year). By wrapping temporal data around a spiral, corresponding periods from different cycles align vertically, making recurring seasonal or periodic patterns immediately visible. This layout provides a compact alternative to long horizontal time series for cyclic data, revealing periodicity and trend simultaneously.

Spiral Time Series Chart rendered with Makie.jl

Renders

Julia source (Makie.jl)

# anyplot.ai
# spiral-timeseries: Spiral Time Series Chart
# Library: makie 0.21.9 | Julia 1.11.9
# Quality: 89/100 | Created: 2026-09-09

using CairoMakie
using Colors
using Random
using Statistics

Random.seed!(42)

# --- Theme tokens -----------------------------------------------------------
const THEME = get(ENV, "ANYPLOT_THEME", "light")
const PAGE_BG = THEME == "light" ? colorant"#FAF8F1" : colorant"#1A1A17"
const INK = THEME == "light" ? colorant"#1A1A17" : colorant"#F0EFE8"
const INK_SOFT = THEME == "light" ? colorant"#4A4A44" : colorant"#B8B7B0"
const IMPRINT_SEQ = cgrad([colorant"#009E73", colorant"#4467A3"])

# --- Data: daily retail sales over 4 years, one revolution per year --------
n_years = 4
days_per_year = 365
n_days = n_years * days_per_year

day_index = 0:(n_days-1)
day_of_year = mod.(day_index, days_per_year)

trend = 1.0 .+ 0.05 .* (day_index ./ days_per_year)
weekly_pattern = 0.10 .* sin.(2π .* day_index ./ 7)
holiday_peak = 0.65 .* exp.(-((day_of_year .- 350) .^ 2) ./ (2 * 16^2))
back_to_school = 0.20 .* exp.(-((day_of_year .- 240) .^ 2) ./ (2 * 20^2))
noise = 3.0 .* randn(n_days)

daily_sales = 40.0 .* trend .* (1.0 .+ holiday_peak .+ back_to_school .+ weekly_pattern) .+ noise
daily_sales = max.(daily_sales, 5.0)

# --- Spiral geometry (Archimedean: constant radial spacing per revolution) -
theta = 2π .* day_index ./ days_per_year        # continuously increasing angle
r_inner = 0.35
r_growth_per_cycle = 0.62 / n_years
radius = r_inner .+ r_growth_per_cycle .* (theta ./ 2π)

phi = theta .+ π / 2                            # rotate so each cycle starts at the top
x = radius .* cos.(phi)
y = radius .* sin.(phi)

# --- Figure -------------------------------------------------------------------
fig = Figure(resolution=(1200, 1200), fontsize=14, backgroundcolor=PAGE_BG)

ax = Axis(
    fig[1, 1];
    title="spiral-timeseries · julia · makie · anyplot.ai",
    titlesize=20,
    titlecolor=INK,
    backgroundcolor=PAGE_BG,
    aspect=DataAspect(),
)
hidedecorations!(ax)
hidespines!(ax)

# Radial grid lines mark month boundaries within each yearly cycle
r_outer = maximum(radius) + 0.05
month_days = round.(Int, range(0, days_per_year, length=13))[1:12]
for d in month_days
    ang = 2π * d / days_per_year + π / 2
    lines!(
        ax,
        [r_inner * cos(ang), r_outer * cos(ang)],
        [r_inner * sin(ang), r_outer * sin(ang)];
        color=(INK_SOFT, 0.25),
        linewidth=1,
    )
end

# Spiral line — color encodes daily sales magnitude. The range is clamped to
# the 5th-95th percentile (rather than full min/max) so day-to-day variation
# stays visible instead of being washed out by the single yearly holiday spike.
color_range = (quantile(daily_sales, 0.05), quantile(daily_sales, 0.95))
lines!(
    ax, x, y;
    color=daily_sales,
    colormap=IMPRINT_SEQ,
    colorrange=color_range,
    linewidth=3.5,
)

# Label the start of each yearly cycle for orientation
for cycle in 0:(n_years-1)
    label_radius = r_inner + r_growth_per_cycle * cycle - 0.05
    text!(
        ax,
        0.0,
        label_radius;
        text="Year $(cycle + 1)",
        color=INK_SOFT,
        fontsize=13,
        align=(:center, :center),
    )
end

Colorbar(
    fig[1, 2],
    limits=color_range,
    colormap=IMPRINT_SEQ,
    label="Daily sales (USD thousands)",
    labelcolor=INK,
    ticklabelcolor=INK_SOFT,
    width=18,
)

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

Part of Spiral Time Series Chart on anyplot.ai.

Other implementations