Asymmetric Error Bars Plot — Apache ECharts

An asymmetric error bar plot displays data points with separate upper and lower error magnitudes, allowing different-sized bars extending above and below each point. This visualization is essential for representing skewed distributions, non-symmetric confidence intervals, or data where uncertainty differs in positive and negative directions. Common applications include percentile-based intervals, log-transformed data, and Bayesian credible intervals.

Asymmetric Error Bars Plot rendered with Apache ECharts

Renders

JavaScript source (Apache ECharts)

// anyplot.ai
// errorbar-asymmetric: Asymmetric Error Bars Plot
// Library: echarts 6.1.0 | JavaScript 22.23.2
// Quality: 89/100 | Created: 2026-09-05

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic) ---------------------------------------
// Quarterly revenue-growth forecast. Upside potential grows every quarter
// (planned product launches expand the ceiling), while downside risk spikes
// in Q3 (a flagged supply-chain risk) before easing back — so the total
// interval widens unevenly toward the end of the horizon, including one
// quarter (Q3) whose low bound dips below 0%.
const quarters = ["Q1", "Q2", "Q3", "Q4", "Q5", "Q6"];
const forecast = [3.2, 4.1, 2.8, 5.5, 4.7, 6.0];
const errorLower = [1.5, 2.0, 3.2, 1.8, 2.5, 3.0];
const errorUpper = [0.8, 1.0, 1.2, 2.5, 3.5, 4.0];

const lowerBound = forecast.map((y, i) => y - errorLower[i]);
const upperBound = forecast.map((y, i) => y + errorUpper[i]);
const rangeData = quarters.map((_, i) => [i, lowerBound[i], upperBound[i]]);

// Total interval width per quarter drives the error bar's stroke weight —
// a redundant visual encoding so the widening-uncertainty trend reads at a
// glance, not just from careful axis reading.
const totalWidth = quarters.map((_, i) => errorLower[i] + errorUpper[i]);
const minWidth = Math.min(...totalWidth);
const maxWidth = Math.max(...totalWidth);
const lineWidthFor = (i) =>
  2 + ((totalWidth[i] - minWidth) / (maxWidth - minWidth)) * 2.5;

const brand = t.palette[0];

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

// --- Render item for the asymmetric whisker (vertical line + end caps) -----
function renderErrorBar(params, api) {
  const xValue = api.value(0);
  const lowPoint = api.coord([xValue, api.value(1)]);
  const highPoint = api.coord([xValue, api.value(2)]);
  const halfWidth = api.size([1, 0])[0] * 0.15;
  const style = {
    stroke: brand,
    lineWidth: lineWidthFor(params.dataIndex),
    lineCap: "round",
  };

  return {
    type: "group",
    children: [
      {
        type: "line",
        shape: { x1: lowPoint[0], y1: lowPoint[1], x2: highPoint[0], y2: highPoint[1] },
        style,
      },
      {
        type: "line",
        shape: {
          x1: lowPoint[0] - halfWidth,
          y1: lowPoint[1],
          x2: lowPoint[0] + halfWidth,
          y2: lowPoint[1],
        },
        style,
      },
      {
        type: "line",
        shape: {
          x1: highPoint[0] - halfWidth,
          y1: highPoint[1],
          x2: highPoint[0] + halfWidth,
          y2: highPoint[1],
        },
        style,
      },
    ],
  };
}

// --- Option -----------------------------------------------------------------
chart.setOption({
  animation: false,
  backgroundColor: "transparent",
  title: {
    text: "errorbar-asymmetric · javascript · echarts · anyplot.ai",
    subtext: "Bars mark the 10th-90th percentile forecast range per quarter",
    left: "center",
    top: 24,
    textStyle: { color: t.ink, fontSize: 27, fontWeight: "bold" },
    subtextStyle: { color: t.inkSoft, fontSize: 15 },
  },
  legend: {
    icon: "roundRect",
    top: 112,
    data: ["10th-90th percentile range", "Point forecast"],
    textStyle: { color: t.inkSoft, fontSize: 16 },
  },
  grid: { left: 90, right: 60, top: 190, bottom: 80 },
  xAxis: {
    type: "category",
    data: quarters,
    name: "Forecast quarter",
    nameLocation: "middle",
    nameGap: 40,
    nameTextStyle: { color: t.inkSoft, fontSize: 16 },
    axisLabel: { color: t.inkSoft, fontSize: 14 },
    axisLine: { lineStyle: { color: t.inkSoft } },
    splitLine: { show: false },
  },
  yAxis: {
    type: "value",
    name: "Revenue growth (%)",
    nameLocation: "middle",
    nameGap: 60,
    nameTextStyle: { color: t.inkSoft, fontSize: 16 },
    axisLabel: { color: t.inkSoft, fontSize: 14, formatter: "{value}%" },
    axisLine: { show: false },
    splitLine: { lineStyle: { color: t.grid } },
  },
  series: [
    {
      name: "10th-90th percentile range",
      type: "custom",
      renderItem: renderErrorBar,
      itemStyle: { color: brand },
      encode: { x: 0, y: [1, 2] },
      data: rangeData,
      z: 2,
    },
    {
      name: "Point forecast",
      type: "scatter",
      data: forecast,
      symbolSize: 20,
      itemStyle: { color: brand, borderColor: t.pageBg, borderWidth: 3 },
      z: 3,
    },
  ],
});

Retrieve this implementation

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

Part of Asymmetric Error Bars Plot on anyplot.ai.

Other implementations