{"spec_id":"chernoff-basic","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// chernoff-basic: Chernoff Faces for Multivariate Data\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 89/100 | Created: 2026-09-02\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Eight financial-health metrics per company, each pre-normalized to 0-1, drive\n// eight facial features. Companies are grouped by sector (colorizes the face\n// outline) so clusters of similar profiles are easy to spot at a glance.\nconst sectorColor = { Tech: t.palette[0], Retail: t.palette[1], Finance: t.palette[2] };\nconst sectors = Object.keys(sectorColor);\n\nconst companies = [\n  { 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 },\n  { 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 },\n  { 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 },\n  { 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 },\n  { 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 },\n  { 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 },\n  { 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 },\n  { 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 },\n  { 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 },\n];\n\n// --- Face drawing (Highcharts core SVGRenderer — no add-on module needed) ---\nfunction ellipsePath(cx, cy, rx, ry) {\n  return [\"M\", cx - rx, cy, \"A\", rx, ry, 0, 1, 0, cx + rx, cy, \"A\", rx, ry, 0, 1, 0, cx - rx, cy, \"Z\"];\n}\n\nfunction drawFace(renderer, cx, cy, size, d) {\n  const faceRx = size * (0.32 + 0.14 * d.revenue_growth);\n  const faceRy = size * (0.36 + 0.14 * d.market_share);\n  const eyeR = size * (0.035 + 0.045 * d.customer_satisfaction);\n  const eyeDx = faceRx * (0.34 + 0.16 * d.employee_engagement);\n  const eyeDy = -faceRy * 0.12;\n  const browSlant = (d.debt_ratio - 0.5) * faceRx * 0.5;\n  const noseLen = size * (0.06 + 0.14 * d.innovation_index);\n  const mouthCurve = (d.profit_margin - 0.5) * size * 0.36;\n  const mouthHalfW = faceRx * (0.38 + 0.22 * d.operating_efficiency);\n  const color = sectorColor[d.sector];\n  const g = renderer.g().add();\n\n  renderer\n    .path(ellipsePath(cx, cy, faceRx, faceRy))\n    .attr({ fill: t.elevatedBg, stroke: color, \"stroke-width\": 3 })\n    .add(g);\n\n  [-1, 1].forEach((side) => {\n    renderer.circle(cx + side * eyeDx, cy + eyeDy, eyeR).attr({ fill: t.ink }).add(g);\n  });\n\n  [-1, 1].forEach((side) => {\n    const bx = cx + side * eyeDx;\n    const by = cy + eyeDy - eyeR * 2.2;\n    const slant = side * browSlant * 0.3;\n    renderer\n      .path([\"M\", bx - eyeR * 1.3, by + slant, \"L\", bx + eyeR * 1.3, by - slant])\n      .attr({ stroke: t.ink, \"stroke-width\": 3, \"stroke-linecap\": \"round\" })\n      .add(g);\n  });\n\n  renderer\n    .path([\"M\", cx, cy - faceRy * 0.05, \"L\", cx - noseLen * 0.3, cy + noseLen, \"L\", cx + noseLen * 0.3, cy + noseLen])\n    .attr({ stroke: t.inkSoft, \"stroke-width\": 2.5, fill: \"none\", \"stroke-linejoin\": \"round\" })\n    .add(g);\n\n  const mouthY = cy + faceRy * 0.55;\n  renderer\n    .path([\"M\", cx - mouthHalfW, mouthY, \"Q\", cx, mouthY - mouthCurve, cx + mouthHalfW, mouthY])\n    .attr({ stroke: t.ink, \"stroke-width\": 3.5, fill: \"none\", \"stroke-linecap\": \"round\" })\n    .add(g);\n\n  renderer\n    .text(d.name, cx, cy + faceRy + 32)\n    .attr({ align: \"center\" })\n    .css({ color: t.inkSoft, fontSize: \"14px\" })\n    .add(g);\n}\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n    events: {\n      load: function () {\n        const renderer = this.renderer;\n        const cols = 3;\n        const rows = 3;\n        const areaTop = 130;\n        const legendAreaHeight = 130;\n        const cellW = this.chartWidth / cols;\n        const cellH = (this.chartHeight - areaTop - legendAreaHeight) / rows;\n        const faceSize = Math.min(cellW, cellH) * 0.72;\n\n        companies.forEach((d, i) => {\n          const col = i % cols;\n          const row = Math.floor(i / cols);\n          const cx = cellW * (col + 0.5);\n          const cy = areaTop + cellH * (row + 0.5);\n          drawFace(renderer, cx, cy, faceSize, d);\n        });\n\n        const legendY = this.chartHeight - 40;\n        let lx = this.chartWidth / 2 - (sectors.length * 140) / 2;\n        sectors.forEach((s) => {\n          renderer.circle(lx + 8, legendY, 8).attr({ fill: sectorColor[s] }).add();\n          renderer.text(s, lx + 24, legendY + 5).css({ color: t.inkSoft, fontSize: \"14px\" }).add();\n          lx += 140;\n        });\n\n        window.__anyplotReady = true;\n      },\n    },\n  },\n  title: {\n    text: \"chernoff-basic · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: \"Company financial-health profiles — 8 metrics mapped to facial features, grouped by sector\",\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n    y: 55,\n  },\n  credits: { enabled: false },\n  xAxis: { visible: false },\n  yAxis: { visible: false },\n  legend: { enabled: false },\n  plotOptions: { series: { animation: false } },\n  series: [],\n});\n"}