Chernoff Faces for Multivariate Data — Highcharts

Chernoff faces visualize multivariate data by mapping each variable to a facial feature (eye size, mouth curvature, face width, nose length, etc.), transforming each observation into a unique cartoon face. This technique leverages humans' innate ability to recognize and distinguish faces, making it easier to identify patterns, clusters, and outliers across multiple dimensions simultaneously.

Chernoff Faces for Multivariate Data rendered with Highcharts

Renders

JavaScript source (Highcharts)

// anyplot.ai
// chernoff-basic: Chernoff Faces for Multivariate Data
// Library: highcharts 12.6.0 | JavaScript 22.23.2
// Quality: 89/100 | Created: 2026-09-02
//# anyplot-orientation: square

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic) ----------------------------------------
// Eight financial-health metrics per company, each pre-normalized to 0-1, drive
// eight facial features. Companies are grouped by sector (colorizes the face
// outline) so clusters of similar profiles are easy to spot at a glance.
const sectorColor = { Tech: t.palette[0], Retail: t.palette[1], Finance: t.palette[2] };
const sectors = Object.keys(sectorColor);

const companies = [
  { name: "Nova Systems", sector: "Tech", revenue_growth: 0.9, market_share: 0.55, profit_margin: 0.78, customer_satisfaction: 0.72, debt_ratio: 0.22, innovation_index: 0.95, employee_engagement: 0.8, operating_efficiency: 0.7 },
  { name: "Pixel Forge", sector: "Tech", revenue_growth: 0.62, market_share: 0.32, profit_margin: 0.48, customer_satisfaction: 0.58, debt_ratio: 0.38, innovation_index: 0.78, employee_engagement: 0.6, operating_efficiency: 0.55 },
  { name: "Quantum Byte", sector: "Tech", revenue_growth: 0.35, market_share: 0.18, profit_margin: 0.22, customer_satisfaction: 0.45, debt_ratio: 0.6, innovation_index: 0.58, employee_engagement: 0.4, operating_efficiency: 0.35 },
  { name: "Urban Mart", sector: "Retail", revenue_growth: 0.5, market_share: 0.72, profit_margin: 0.34, customer_satisfaction: 0.75, debt_ratio: 0.48, innovation_index: 0.28, employee_engagement: 0.65, operating_efficiency: 0.6 },
  { name: "Green Grocer", sector: "Retail", revenue_growth: 0.3, market_share: 0.45, profit_margin: 0.18, customer_satisfaction: 0.68, debt_ratio: 0.62, innovation_index: 0.18, employee_engagement: 0.5, operating_efficiency: 0.4 },
  { name: "Trend Outlet", sector: "Retail", revenue_growth: 0.7, market_share: 0.38, profit_margin: 0.52, customer_satisfaction: 0.55, debt_ratio: 0.33, innovation_index: 0.42, employee_engagement: 0.58, operating_efficiency: 0.62 },
  { name: "Harbor Capital", sector: "Finance", revenue_growth: 0.28, market_share: 0.58, profit_margin: 0.85, customer_satisfaction: 0.48, debt_ratio: 0.14, innovation_index: 0.35, employee_engagement: 0.7, operating_efficiency: 0.8 },
  { name: "Anchor Trust", sector: "Finance", revenue_growth: 0.18, market_share: 0.4, profit_margin: 0.63, customer_satisfaction: 0.55, debt_ratio: 0.26, innovation_index: 0.24, employee_engagement: 0.55, operating_efficiency: 0.68 },
  { name: "Ledger Union", sector: "Finance", revenue_growth: 0.48, market_share: 0.25, profit_margin: 0.7, customer_satisfaction: 0.4, debt_ratio: 0.42, innovation_index: 0.46, employee_engagement: 0.48, operating_efficiency: 0.58 },
];

// --- Face drawing (Highcharts core SVGRenderer — no add-on module needed) ---
function ellipsePath(cx, cy, rx, ry) {
  return ["M", cx - rx, cy, "A", rx, ry, 0, 1, 0, cx + rx, cy, "A", rx, ry, 0, 1, 0, cx - rx, cy, "Z"];
}

function drawFace(renderer, cx, cy, size, d) {
  const faceRx = size * (0.32 + 0.14 * d.revenue_growth);
  const faceRy = size * (0.36 + 0.14 * d.market_share);
  const eyeR = size * (0.035 + 0.045 * d.customer_satisfaction);
  const eyeDx = faceRx * (0.34 + 0.16 * d.employee_engagement);
  const eyeDy = -faceRy * 0.12;
  const browSlant = (d.debt_ratio - 0.5) * faceRx * 0.5;
  const noseLen = size * (0.06 + 0.14 * d.innovation_index);
  const mouthCurve = (d.profit_margin - 0.5) * size * 0.36;
  const mouthHalfW = faceRx * (0.38 + 0.22 * d.operating_efficiency);
  const color = sectorColor[d.sector];
  const g = renderer.g().add();

  renderer
    .path(ellipsePath(cx, cy, faceRx, faceRy))
    .attr({ fill: t.elevatedBg, stroke: color, "stroke-width": 3 })
    .add(g);

  [-1, 1].forEach((side) => {
    renderer.circle(cx + side * eyeDx, cy + eyeDy, eyeR).attr({ fill: t.ink }).add(g);
  });

  [-1, 1].forEach((side) => {
    const bx = cx + side * eyeDx;
    const by = cy + eyeDy - eyeR * 2.2;
    const slant = side * browSlant * 0.3;
    renderer
      .path(["M", bx - eyeR * 1.3, by + slant, "L", bx + eyeR * 1.3, by - slant])
      .attr({ stroke: t.ink, "stroke-width": 3, "stroke-linecap": "round" })
      .add(g);
  });

  renderer
    .path(["M", cx, cy - faceRy * 0.05, "L", cx - noseLen * 0.3, cy + noseLen, "L", cx + noseLen * 0.3, cy + noseLen])
    .attr({ stroke: t.inkSoft, "stroke-width": 2.5, fill: "none", "stroke-linejoin": "round" })
    .add(g);

  const mouthY = cy + faceRy * 0.55;
  renderer
    .path(["M", cx - mouthHalfW, mouthY, "Q", cx, mouthY - mouthCurve, cx + mouthHalfW, mouthY])
    .attr({ stroke: t.ink, "stroke-width": 3.5, fill: "none", "stroke-linecap": "round" })
    .add(g);

  renderer
    .text(d.name, cx, cy + faceRy + 32)
    .attr({ align: "center" })
    .css({ color: t.inkSoft, fontSize: "14px" })
    .add(g);
}

// --- Chart -------------------------------------------------------------------
Highcharts.chart("container", {
  chart: {
    backgroundColor: "transparent",
    animation: false,
    style: { fontFamily: "inherit" },
    events: {
      load: function () {
        const renderer = this.renderer;
        const cols = 3;
        const rows = 3;
        const areaTop = 130;
        const legendAreaHeight = 130;
        const cellW = this.chartWidth / cols;
        const cellH = (this.chartHeight - areaTop - legendAreaHeight) / rows;
        const faceSize = Math.min(cellW, cellH) * 0.72;

        companies.forEach((d, i) => {
          const col = i % cols;
          const row = Math.floor(i / cols);
          const cx = cellW * (col + 0.5);
          const cy = areaTop + cellH * (row + 0.5);
          drawFace(renderer, cx, cy, faceSize, d);
        });

        const legendY = this.chartHeight - 40;
        let lx = this.chartWidth / 2 - (sectors.length * 140) / 2;
        sectors.forEach((s) => {
          renderer.circle(lx + 8, legendY, 8).attr({ fill: sectorColor[s] }).add();
          renderer.text(s, lx + 24, legendY + 5).css({ color: t.inkSoft, fontSize: "14px" }).add();
          lx += 140;
        });

        window.__anyplotReady = true;
      },
    },
  },
  title: {
    text: "chernoff-basic · javascript · highcharts · anyplot.ai",
    style: { color: t.ink, fontSize: "22px", fontWeight: "600" },
  },
  subtitle: {
    text: "Company financial-health profiles — 8 metrics mapped to facial features, grouped by sector",
    style: { color: t.inkSoft, fontSize: "14px" },
    y: 55,
  },
  credits: { enabled: false },
  xAxis: { visible: false },
  yAxis: { visible: false },
  legend: { enabled: false },
  plotOptions: { series: { animation: false } },
  series: [],
});

Retrieve this implementation

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

Part of Chernoff Faces for Multivariate Data on anyplot.ai.

Other implementations