Event Timeline — ggplot2

A timeline visualization that displays events and milestones along a temporal axis. Events are represented as points or markers with accompanying labels, making it easy to understand the sequence and timing of events. This plot type excels at showing chronological progressions and is particularly effective for communicating project phases, historical events, or any time-ordered sequence of occurrences.

Event Timeline rendered with ggplot2

Renders

R source (ggplot2)

#' anyplot.ai
#' timeline-basic: Event Timeline
#' Library: ggplot2 3.5.1 | R 4.4.1
#' Quality: 89/100 | Created: 2026-09-09

library(ggplot2)
library(dplyr)
library(ragg)

set.seed(42)

# --- Theme tokens -------------------------------------------------------
THEME       <- Sys.getenv("ANYPLOT_THEME", "light")
PAGE_BG     <- if (THEME == "light") "#FAF8F1" else "#1A1A17"
INK         <- if (THEME == "light") "#1A1A17" else "#F0EFE8"
INK_SOFT    <- if (THEME == "light") "#4A4A44" else "#B8B7B0"

IMPRINT_PALETTE <- c(
  "#009E73", # 1 - Team (brand green)
  "#C475FD", # 2 - Product (lavender)
  "#4467A3", # 3 - Funding (blue)
  "#BD8233"  # 4 - Expansion (ochre)
)

# --- Data: company milestones since founding -----------------------------
milestones <- tibble::tibble(
  date = as.Date(c(
    "2018-03-01", "2018-09-15", "2019-05-01", "2019-11-20",
    "2020-04-01", "2020-10-05", "2021-06-15", "2021-12-01",
    "2022-03-10", "2022-08-20", "2023-02-14", "2023-09-01",
    "2024-05-15"
  )),
  event = c(
    "Company Founded", "Seed Funding", "MVP Launch", "Series A",
    "10th Employee", "Mobile App Launch", "European Expansion", "Series B",
    "100th Employee", "Enterprise Tier Launch", "Asia-Pacific Expansion",
    "Series C", "IPO Announcement"
  ),
  category = factor(
    c(
      "Team", "Funding", "Product", "Funding",
      "Team", "Product", "Expansion", "Funding",
      "Team", "Product", "Expansion", "Funding", "Funding"
    ),
    levels = c("Team", "Product", "Funding", "Expansion")
  )
) %>%
  arrange(date) %>%
  mutate(
    above = row_number() %% 2 == 1,
    label = paste0(event, "\n", format(date, "%b %Y"))
  ) %>%
  group_by(above) %>%
  mutate(tier = (row_number() - 1) %% 3 + 1) %>%
  ungroup() %>%
  mutate(label_y = (0.9 + (tier - 1) * 0.7) * ifelse(above, 1, -1))

# --- Plot -----------------------------------------------------------------
p <- ggplot(milestones) +
  geom_hline(yintercept = 0, color = INK_SOFT, linewidth = 0.4) +
  geom_segment(
    aes(x = date, xend = date, y = 0, yend = label_y, color = category),
    linewidth = 0.7
  ) +
  geom_point(
    aes(x = date, y = 0, color = category),
    size = 3.2
  ) +
  geom_text(
    aes(
      x = date, y = label_y, label = label,
      vjust = ifelse(above, -0.15, 1.15)
    ),
    color = INK, size = 3, lineheight = 0.95, fontface = "plain"
  ) +
  scale_color_manual(values = IMPRINT_PALETTE, name = "Category") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
  scale_y_continuous(limits = c(-2.7, 2.7)) +
  labs(
    title = "Company Milestones · timeline-basic · r · ggplot2 · anyplot.ai",
    x = "Year",
    y = NULL
  ) +
  theme_minimal(base_size = 8) +
  theme(
    plot.background     = element_rect(fill = PAGE_BG, color = PAGE_BG),
    panel.background    = element_rect(fill = PAGE_BG, color = NA),
    panel.grid.major.x  = element_line(color = INK, linewidth = 0.2),
    panel.grid.minor.x  = element_blank(),
    panel.grid.major.y  = element_blank(),
    panel.grid.minor.y  = element_blank(),
    axis.title.x        = element_text(color = INK, size = 10),
    axis.text.x         = element_text(color = INK_SOFT, size = 8),
    axis.title.y        = element_blank(),
    axis.text.y         = element_blank(),
    axis.ticks          = element_blank(),
    plot.title          = element_text(color = INK, size = 12),
    legend.position     = "bottom",
    legend.background   = element_rect(fill = PAGE_BG, color = NA),
    legend.text         = element_text(color = INK_SOFT, size = 8),
    legend.title        = element_text(color = INK, size = 10),
    plot.margin         = margin(t = 12, r = 20, b = 8, l = 20)
  ) +
  coord_cartesian(clip = "off")

# --- Save -------------------------------------------------------------------
ggsave(
  filename = sprintf("plot-%s.png", THEME),
  plot     = p,
  device   = ragg::agg_png,
  width    = 8,
  height   = 4.5,
  units    = "in",
  dpi      = 400
)

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/timeline-basic/ggplot2/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": "timeline-basic",
  "language": "r",
  "library": "ggplot2",
  "page": "https://anyplot.ai/timeline-basic/r/ggplot2",
  "hub": "https://anyplot.ai/timeline-basic",
  "code_json": "https://api.anyplot.ai/specs/timeline-basic/ggplot2/code",
  "spec_json": "https://api.anyplot.ai/specs/timeline-basic",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/timeline-basic/r/ggplot2/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/timeline-basic/r/ggplot2/plot-dark.png",
  "quality_score": 89.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Event Timeline on anyplot.ai.

Other implementations