Basic Pyramid Chart — Apache ECharts

A pyramid chart displays two opposing horizontal bar charts that share a central axis, creating a pyramid or butterfly shape. This visualization is ideal for comparing two related metrics across the same categories, revealing asymmetries and patterns in bidirectional data. Most commonly used for population pyramids showing age-gender distributions.

Basic Pyramid Chart rendered with Apache ECharts

Renders

JavaScript source (Apache ECharts)

// anyplot.ai
// pyramid-basic: Basic Pyramid Chart
// Library: echarts 6.1.0 | JavaScript 22.23.2
// Quality: 95/100 | Created: 2026-09-05

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic) ---------------------------------------
// Population by age group, thousands of residents
const ageGroups = ["0-9", "10-19", "20-29", "30-39", "40-49", "50-59", "60-69", "70+"];
const malePopulation = [42, 45, 47, 44, 39, 33, 24, 15];
const femalePopulation = [40, 43, 45, 43, 40, 36, 29, 22];
const axisMax = 50;

// --- Init -------------------------------------------------------------------
const chart = echarts.init(document.getElementById("container"));

// --- Option -----------------------------------------------------------------
// Three-grid layout: left bars (male) + centered label column (age groups) +
// right bars (female), so the shared category axis sits on the pyramid's
// central spine instead of on one outer edge.
chart.setOption({
  animation: false,
  backgroundColor: "transparent",
  title: {
    text: "pyramid-basic · javascript · echarts · anyplot.ai",
    left: "center",
    top: 30,
    textStyle: { color: t.ink, fontSize: 22 },
  },
  legend: {
    data: ["Male", "Female"],
    top: 78,
    textStyle: { color: t.inkSoft, fontSize: 14 },
  },
  tooltip: {
    trigger: "axis",
    axisPointer: { type: "shadow" },
    valueFormatter: (value) => `${Math.abs(value)}k`,
  },
  grid: [
    { left: "4%", width: "42%", top: 150, bottom: 120 },
    { left: "46%", width: "8%", top: 150, bottom: 120 },
    { left: "54%", width: "42%", top: 150, bottom: 120 },
  ],
  xAxis: [
    {
      type: "value",
      gridIndex: 0,
      inverse: true,
      min: 0,
      max: axisMax,
      name: "Population (thousands)",
      nameLocation: "middle",
      nameGap: 40,
      nameTextStyle: { color: t.inkSoft, fontSize: 13 },
      axisLabel: { color: t.inkSoft, fontSize: 14, formatter: "{value}k" },
      axisLine: { lineStyle: { color: t.grid, width: 1 } },
      splitLine: { lineStyle: { color: t.grid } },
    },
    { type: "value", gridIndex: 1, min: 0, max: 1, show: false },
    {
      type: "value",
      gridIndex: 2,
      min: 0,
      max: axisMax,
      name: "Population (thousands)",
      nameLocation: "middle",
      nameGap: 40,
      nameTextStyle: { color: t.inkSoft, fontSize: 13 },
      axisLabel: { color: t.inkSoft, fontSize: 14, formatter: "{value}k" },
      axisLine: { lineStyle: { color: t.grid, width: 1 } },
      splitLine: { lineStyle: { color: t.grid } },
    },
  ],
  yAxis: [
    { type: "category", gridIndex: 0, data: ageGroups, show: false },
    {
      type: "category",
      gridIndex: 1,
      data: ageGroups,
      position: "left",
      axisLine: { show: false },
      axisTick: { show: false },
      axisLabel: { color: t.ink, fontSize: 14, align: "left", margin: 0 },
    },
    { type: "category", gridIndex: 2, data: ageGroups, show: false },
  ],
  series: [
    {
      name: "Male",
      type: "bar",
      xAxisIndex: 0,
      yAxisIndex: 0,
      data: malePopulation,
      barWidth: "65%",
      itemStyle: { color: t.palette[0] },
      // Subtle highlight over the two oldest cohorts, where female population
      // overtakes male — the most notable feature in this dataset.
      markArea: {
        silent: true,
        itemStyle: { color: t.amber, opacity: 0.08 },
        data: [[{ yAxis: "60-69" }, { yAxis: "70+" }]],
      },
    },
    {
      name: "Female",
      type: "bar",
      xAxisIndex: 2,
      yAxisIndex: 2,
      data: femalePopulation,
      barWidth: "65%",
      itemStyle: { color: t.palette[1] },
      markArea: {
        silent: true,
        itemStyle: { color: t.amber, opacity: 0.08 },
        label: {
          show: true,
          position: "insideTop",
          formatter: "Female exceeds\nMale past 60",
          color: t.inkSoft,
          fontSize: 12,
          lineHeight: 15,
        },
        data: [[{ yAxis: "60-69" }, { yAxis: "70+" }]],
      },
    },
  ],
});

Retrieve this implementation

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

Part of Basic Pyramid Chart on anyplot.ai.

Other implementations