Faceted Grid Plot — Apache ECharts

A grid of subplots where each cell shows the same type of plot for a different subset of data, split by one or two categorical variables. Enables systematic comparison across multiple dimensions.

Faceted Grid Plot rendered with Apache ECharts

Renders

JavaScript source (Apache ECharts)

// anyplot.ai
// facet-grid: Faceted Grid Plot
// Library: echarts 6.1.0 | JavaScript 22.23.2
// Quality: 93/100 | Created: 2026-09-05

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic, penguin-style measurements) -----------
// Bill length (mm) vs. body mass (g), faceted by species (columns) and
// sex (rows) — mirrors the Palmer Penguins scenario suggested by the spec.
function makeLcg(seed) {
  let state = seed;
  return function () {
    state = (state * 1664525 + 1013904223) % 4294967296;
    return state / 4294967296;
  };
}
const rng = makeLcg(42);

function randNormal(mean, sd) {
  const u1 = Math.max(rng(), 1e-9);
  const u2 = rng();
  const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
  return mean + z * sd;
}

const species = ["Adelie", "Chinstrap", "Gentoo"];
const sexes = ["Female", "Male"];
const speciesStats = {
  Adelie: { bill: 38.8, billSd: 2.2, mass: 3550, massSd: 380 },
  Chinstrap: { bill: 48.8, billSd: 3.0, mass: 3730, massSd: 380 },
  Gentoo: { bill: 47.5, billSd: 3.1, mass: 5076, massSd: 500 },
};
const sexEffect = {
  Female: { bill: -1.0, mass: -300 },
  Male: { bill: 1.0, mass: 300 },
};
const pointsPerGroup = 60;

// grid order is row-major: row = sex index, col = species index
const facetData = [];
for (const sex of sexes) {
  for (const sp of species) {
    const stats = speciesStats[sp];
    const effect = sexEffect[sex];
    const points = [];
    for (let i = 0; i < pointsPerGroup; i++) {
      const bill = randNormal(stats.bill + effect.bill, stats.billSd);
      const mass = randNormal(stats.mass + effect.mass, stats.massSd);
      points.push([bill, mass]);
    }
    facetData.push(points);
  }
}

// --- Layout (CSS px within the 1600x900 landscape mount) --------------------
const gridLeft = 110;
const gridRight = 1540;
const gridTop = 100;
const gridBottom = 830;
const colGap = 20;
const rowGap = 20;
const colWidth = (gridRight - gridLeft - 2 * colGap) / 3;
const rowHeight = (gridBottom - gridTop - rowGap) / 2;
const colLefts = [0, 1, 2].map((c) => gridLeft + c * (colWidth + colGap));
const rowTops = [0, 1].map((r) => gridTop + r * (rowHeight + rowGap));

const X_MIN = 28;
const X_MAX = 60;
const Y_MIN = 2000;
const Y_MAX = 7000;

const grids = [];
const xAxes = [];
const yAxes = [];
const series = [];

for (let r = 0; r < sexes.length; r++) {
  for (let c = 0; c < species.length; c++) {
    const idx = r * species.length + c;
    grids.push({
      left: colLefts[c],
      top: rowTops[r],
      width: colWidth,
      height: rowHeight,
    });
    xAxes.push({
      gridIndex: idx,
      type: "value",
      min: X_MIN,
      max: X_MAX,
      interval: 8,
      axisLabel: {
        show: r === sexes.length - 1,
        color: t.inkSoft,
        fontSize: 13,
      },
      axisLine: { lineStyle: { color: t.inkSoft } },
      axisTick: { show: false },
      splitLine: { show: true, lineStyle: { color: t.grid } },
    });
    yAxes.push({
      gridIndex: idx,
      type: "value",
      min: Y_MIN,
      max: Y_MAX,
      interval: 1250,
      axisLabel: {
        show: c === 0,
        color: t.inkSoft,
        fontSize: 13,
        formatter: (v) => `${v / 1000}k`,
      },
      axisLine: { lineStyle: { color: t.inkSoft } },
      axisTick: { show: false },
      splitLine: { show: true, lineStyle: { color: t.grid } },
    });
    series.push({
      type: "scatter",
      xAxisIndex: idx,
      yAxisIndex: idx,
      data: facetData[idx],
      symbolSize: 9,
      itemStyle: { color: t.palette[0], opacity: 0.75 },
    });
  }
}

// --- Facet strip labels + shared axis titles (graphic overlay) -------------
const graphic = [];
species.forEach((sp, c) => {
  graphic.push({
    type: "text",
    left: colLefts[c] + colWidth / 2,
    top: 78,
    style: {
      text: sp,
      fill: t.ink,
      font: "600 15px sans-serif",
      align: "center",
      backgroundColor: t.elevatedBg,
      padding: [6, 10],
    },
  });
});
sexes.forEach((sex, r) => {
  graphic.push({
    type: "text",
    left: gridRight + 12,
    top: rowTops[r] + rowHeight / 2,
    rotation: Math.PI / 2,
    style: {
      text: sex,
      fill: t.ink,
      font: "600 15px sans-serif",
      align: "center",
      backgroundColor: t.elevatedBg,
      padding: [6, 10],
    },
  });
});
graphic.push({
  type: "text",
  left: (gridLeft + gridRight) / 2,
  top: 865,
  style: {
    text: "Bill Length (mm)",
    fill: t.ink,
    font: "14px sans-serif",
    align: "center",
  },
});
graphic.push({
  type: "text",
  left: 30,
  top: (gridTop + gridBottom) / 2,
  rotation: -Math.PI / 2,
  style: {
    text: "Body Mass (g)",
    fill: t.ink,
    font: "14px sans-serif",
    align: "center",
  },
});

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

chart.setOption({
  animation: false,
  backgroundColor: "transparent",
  title: {
    text: "Penguin Measurements · facet-grid · javascript · echarts · anyplot.ai",
    left: "center",
    top: 12,
    textStyle: { color: t.ink, fontSize: 21 },
  },
  grid: grids,
  xAxis: xAxes,
  yAxis: yAxes,
  series: series,
  graphic: graphic,
});

Retrieve this implementation

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

Part of Faceted Grid Plot on anyplot.ai.

Other implementations