Pediatric Growth Chart with Percentile Curves — Apache ECharts

A WHO/CDC-style growth chart displaying smooth percentile curves (3rd, 10th, 25th, 50th, 75th, 90th, 97th) as colored bands, with individual patient data points overlaid and connected by a line. This chart is a standard clinical tool for monitoring child development metrics such as height, weight, or BMI across age. It enables quick visual assessment of whether a child's growth trajectory falls within expected population ranges.

Pediatric Growth Chart with Percentile Curves rendered with Apache ECharts

JavaScript source (Apache ECharts)

// anyplot.ai
// line-growth-percentile: Pediatric Growth Chart with Percentile Curves
// Library: echarts 5.5.1 | JavaScript 22.22.3
// Quality: 91/100 | Created: 2026-06-20

const t = window.ANYPLOT_TOKENS;

// Reference ages (months) — every 3 months, 0–36
const ages = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36];
const ageLabels = ages.map(String);

// Approximate WHO weight-for-age reference curves for boys (kg)
const refP3  = [2.5, 4.9, 6.4, 7.5, 8.2,  8.8,  9.2,  9.6, 10.0, 10.3, 10.7, 11.0, 11.3];
const refP10 = [2.8, 5.3, 6.9, 8.0, 8.8,  9.4,  9.9, 10.3, 10.8, 11.1, 11.5, 11.9, 12.2];
const refP25 = [3.0, 5.7, 7.4, 8.6, 9.5, 10.1, 10.7, 11.1, 11.5, 12.0, 12.4, 12.8, 13.2];
const refP50 = [3.3, 6.2, 7.9, 9.2, 10.2, 10.9, 11.5, 12.0, 12.5, 13.0, 13.4, 13.9, 14.4];
const refP75 = [3.7, 6.7, 8.5, 9.9, 10.9, 11.7, 12.4, 12.9, 13.4, 14.0, 14.5, 15.0, 15.6];
const refP90 = [4.0, 7.2, 9.1, 10.5, 11.6, 12.4, 13.2, 13.8, 14.3, 15.0, 15.5, 16.1, 16.7];
const refP97 = [4.3, 7.7, 9.7, 11.2, 12.3, 13.2, 14.0, 14.7, 15.3, 16.0, 16.5, 17.2, 17.9];

// Stacking differences (each band = upper minus lower percentile)
const diff = (u, l) => u.map((v, i) => +(v - l[i]).toFixed(2));
const band3_10  = diff(refP10, refP3);
const band10_25 = diff(refP25, refP10);
const band25_75 = diff(refP75, refP25);
const band75_90 = diff(refP90, refP75);
const band90_97 = diff(refP97, refP90);

// Patient measurements aligned to reference age grid (null = no visit that month)
// Boy tracking around the 65th percentile at well-child visits
const patientData = [3.5, null, 8.2, 9.8, 11.0, 11.9, 12.6, null, 13.8, null, 15.2, null, 16.0];

const boyBlue = "#4467A3";

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

chart.setOption({
  animation: false,
  color: t.palette,
  backgroundColor: "transparent",

  title: {
    text: "line-growth-percentile · javascript · echarts · anyplot.ai",
    subtext: "WHO Weight-for-Age Reference  ·  Boys 0–36 Months",
    left: "center",
    top: 16,
    textStyle: { color: t.ink, fontSize: 22, fontWeight: "bold" },
    subtextStyle: { color: t.inkSoft, fontSize: 14 },
  },

  legend: {
    data: ["Patient (Boy, 2024)", "50th Percentile (Median)"],
    bottom: 20,
    itemGap: 40,
    textStyle: { color: t.inkSoft, fontSize: 15 },
  },

  grid: { left: 90, right: 120, top: 100, bottom: 78 },

  xAxis: {
    type: "category",
    data: ageLabels,
    name: "Age (months)",
    nameLocation: "middle",
    nameGap: 48,
    nameTextStyle: { color: t.inkSoft, fontSize: 16 },
    axisLabel: { color: t.inkSoft, fontSize: 14 },
    axisLine: { lineStyle: { color: t.inkSoft } },
    axisTick: { lineStyle: { color: t.inkSoft } },
    splitLine: { show: false },
  },

  yAxis: {
    type: "value",
    name: "Weight (kg)",
    nameLocation: "middle",
    nameGap: 55,
    nameTextStyle: { color: t.inkSoft, fontSize: 16 },
    min: 0,
    max: 20,
    interval: 2,
    axisLabel: { color: t.inkSoft, fontSize: 14 },
    axisLine: { lineStyle: { color: t.inkSoft } },
    axisTick: { lineStyle: { color: t.inkSoft } },
    splitLine: { lineStyle: { color: t.grid } },
  },

  series: [
    // ── Stacked band fills (bottom to top) ──────────────────────────────────
    // Base: invisible fill from 0 to P3
    {
      type: "line", stack: "perc", silent: true, legendHoverLink: false,
      data: refP3, symbol: "none", smooth: true,
      lineStyle: { opacity: 0 },
      areaStyle: { color: "transparent" },
    },
    // P3–P10 (outer lower band — moderate fill)
    {
      type: "line", stack: "perc", silent: true, legendHoverLink: false,
      data: band3_10, symbol: "none", smooth: true,
      lineStyle: { opacity: 0 },
      areaStyle: { color: "rgba(68,103,163,0.45)" },
    },
    // P10–P25
    {
      type: "line", stack: "perc", silent: true, legendHoverLink: false,
      data: band10_25, symbol: "none", smooth: true,
      lineStyle: { opacity: 0 },
      areaStyle: { color: "rgba(68,103,163,0.28)" },
    },
    // P25–P75 (lightest — the normal range)
    {
      type: "line", stack: "perc", silent: true, legendHoverLink: false,
      data: band25_75, symbol: "none", smooth: true,
      lineStyle: { opacity: 0 },
      areaStyle: { color: "rgba(68,103,163,0.13)" },
    },
    // P75–P90
    {
      type: "line", stack: "perc", silent: true, legendHoverLink: false,
      data: band75_90, symbol: "none", smooth: true,
      lineStyle: { opacity: 0 },
      areaStyle: { color: "rgba(68,103,163,0.28)" },
    },
    // P90–P97 (outer upper band — moderate fill)
    {
      type: "line", stack: "perc", silent: true, legendHoverLink: false,
      data: band90_97, symbol: "none", smooth: true,
      lineStyle: { opacity: 0 },
      areaStyle: { color: "rgba(68,103,163,0.45)" },
    },

    // ── Percentile reference lines ───────────────────────────────────────────
    {
      type: "line", name: "P3", silent: true, legendHoverLink: false,
      data: refP3, symbol: "none", smooth: true,
      lineStyle: { color: boyBlue, width: 1, opacity: 0.55, type: "dashed" },
      endLabel: { show: true, formatter: () => "P3",
                  color: boyBlue, fontSize: 13, opacity: 0.75 },
    },
    {
      type: "line", name: "P10", silent: true, legendHoverLink: false,
      data: refP10, symbol: "none", smooth: true,
      lineStyle: { color: boyBlue, width: 1, opacity: 0.55 },
      endLabel: { show: true, formatter: () => "P10",
                  color: boyBlue, fontSize: 13, opacity: 0.75 },
    },
    {
      type: "line", name: "P25", silent: true, legendHoverLink: false,
      data: refP25, symbol: "none", smooth: true,
      lineStyle: { color: boyBlue, width: 1.5, opacity: 0.65 },
      endLabel: { show: true, formatter: () => "P25",
                  color: boyBlue, fontSize: 13, opacity: 0.8 },
    },
    // P50 median — thicker, shown in legend
    {
      type: "line", name: "50th Percentile (Median)", silent: true,
      data: refP50, symbol: "none", smooth: true,
      lineStyle: { color: boyBlue, width: 3 },
      itemStyle: { color: boyBlue },
      endLabel: { show: true, formatter: () => "P50",
                  color: boyBlue, fontSize: 14, fontWeight: "bold" },
    },
    {
      type: "line", name: "P75", silent: true, legendHoverLink: false,
      data: refP75, symbol: "none", smooth: true,
      lineStyle: { color: boyBlue, width: 1.5, opacity: 0.65 },
      endLabel: { show: true, formatter: () => "P75",
                  color: boyBlue, fontSize: 13, opacity: 0.8 },
    },
    {
      type: "line", name: "P90", silent: true, legendHoverLink: false,
      data: refP90, symbol: "none", smooth: true,
      lineStyle: { color: boyBlue, width: 1, opacity: 0.55 },
      endLabel: { show: true, formatter: () => "P90",
                  color: boyBlue, fontSize: 13, opacity: 0.75 },
    },
    {
      type: "line", name: "P97", silent: true, legendHoverLink: false,
      data: refP97, symbol: "none", smooth: true,
      lineStyle: { color: boyBlue, width: 1, opacity: 0.55, type: "dashed" },
      endLabel: { show: true, formatter: () => "P97",
                  color: boyBlue, fontSize: 13, opacity: 0.75 },
    },

    // ── Patient trajectory — Imprint position 1 (always first categorical) ──
    {
      type: "line",
      name: "Patient (Boy, 2024)",
      data: patientData,
      connectNulls: true,
      symbol: "circle",
      symbolSize: 12,
      lineStyle: { color: t.palette[0], width: 3 },
      itemStyle: {
        color: t.palette[0],
        borderColor: t.pageBg,
        borderWidth: 2,
      },
    },
  ],
});

Part of Pediatric Growth Chart with Percentile Curves on anyplot.ai.

Other implementations