Basic Q-Q Plot — MUI X Charts

A Q-Q (Quantile-Quantile) plot compares the distribution of a dataset against a theoretical distribution (typically normal) or another dataset. Points are plotted by matching sample quantiles to theoretical quantiles, with a diagonal reference line indicating perfect distribution match. Deviations from the line reveal distribution characteristics such as skewness, heavy tails, and outliers.

Basic Q-Q Plot rendered with MUI X Charts

Renders

JavaScript source (MUI X Charts)

// anyplot.ai
// qq-basic: Basic Q-Q Plot
// Library: muix 7.29.1 | JavaScript 22.23.1
// Quality: 90/100 | Created: 2026-07-24

import { ChartContainer } from "@mui/x-charts/ChartContainer";
import { ScatterPlot } from "@mui/x-charts/ScatterChart";
import { ChartsXAxis } from "@mui/x-charts/ChartsXAxis";
import { ChartsYAxis } from "@mui/x-charts/ChartsYAxis";
import { ChartsGrid } from "@mui/x-charts/ChartsGrid";
import { useXScale, useYScale } from "@mui/x-charts/hooks";

const t = window.ANYPLOT_TOKENS;
const { width, height } = window.ANYPLOT_SIZE;

// Deterministic LCG (seed 42) — no Math.random() in browser harness
let _seed = 42;
function lcg() {
  _seed = (1664525 * _seed + 1013904223) >>> 0;
  return _seed / 4294967296;
}
function randn() {
  const u = lcg(), v = lcg();
  return Math.sqrt(-2 * Math.log(u + 1e-15)) * Math.cos(2 * Math.PI * v);
}

// Inverse standard-normal CDF (Acklam's rational approximation, |err| < 1.15e-9)
function invNorm(p) {
  const a = [-3.969683028665376e+01, 2.209460984245205e+02, -2.759285104469687e+02, 1.383577518672690e+02, -3.066479806614716e+01, 2.506628277459239e+00];
  const b = [-5.447609879822406e+01, 1.615858368580409e+02, -1.556989798598866e+02, 6.680131188771972e+01, -1.328068155288572e+01];
  const c = [-7.784894002430293e-03, -3.223964580411365e-01, -2.400758277161838e+00, -2.549732539343734e+00, 4.374664141464968e+00, 2.938163982698783e+00];
  const d = [7.784695709041462e-03, 3.224671290700398e-01, 2.445134137142996e+00, 3.754408661907416e+00];
  const pLow = 0.02425;
  if (p < pLow) {
    const q = Math.sqrt(-2 * Math.log(p));
    return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
      ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
  }
  if (p <= 1 - pLow) {
    const q = p - 0.5, r = q * q;
    return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q /
      (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
  }
  const q = Math.sqrt(-2 * Math.log(1 - p));
  return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
    ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
}

// Order-fulfillment time (hours), right-skewed lognormal process — a classic
// "does this follow a Normal distribution" QC question. Deliberately non-normal
// so the plot demonstrates a real curvature away from the diagonal.
const N = 60;
const MU = 3.4, SIGMA = 0.35;
const rawSample = Array.from({ length: N }, () => Math.exp(MU + SIGMA * randn()));
const sorted = [...rawSample].sort((x, y) => x - y);

const mean = sorted.reduce((s, v) => s + v, 0) / N;
const variance = sorted.reduce((s, v) => s + (v - mean) ** 2, 0) / (N - 1);
const std = Math.sqrt(variance);

// Blom plotting positions, standardized sample quantiles so a perfectly Normal
// sample lands exactly on y = x.
const points = sorted.map((v, i) => {
  const p = (i + 1 - 0.375) / (N + 0.25);
  const x = invNorm(p);
  const y = (v - mean) / std;
  return { x, y, id: i };
});

const allValues = points.flatMap((pt) => [pt.x, pt.y]);
const rangeMin = Math.min(...allValues);
const rangeMax = Math.max(...allValues);
const pad = (rangeMax - rangeMin) * 0.08;
const axMin = rangeMin - pad;
const axMax = rangeMax + pad;

// Diagonal y = x reference line — rendered via MUI X axis scale hooks
function ReferenceLine() {
  const xScale = useXScale();
  const yScale = useYScale();
  if (!xScale || !yScale) return null;
  return (
    <line
      x1={xScale(axMin)} y1={yScale(axMin)}
      x2={xScale(axMax)} y2={yScale(axMax)}
      stroke={t.ink}
      strokeWidth={2.5}
      strokeDasharray="10,8"
      opacity={0.5}
    />
  );
}

const series = [
  {
    type: "scatter",
    id: "sample",
    label: "Sample vs. Normal",
    color: t.palette[0],
    markerSize: 9,
    data: points,
  },
];

const TITLE = "qq-basic · javascript · muix · anyplot.ai";
const MARGIN = { top: 80, right: 60, bottom: 96, left: 110 };

export default function Chart() {
  return (
    <ChartContainer
      width={width}
      height={height}
      margin={MARGIN}
      series={series}
      xAxis={[{
        scaleType: "linear",
        min: axMin,
        max: axMax,
        label: "Theoretical Quantiles",
        tickLabelStyle: { fontSize: 14, fill: t.inkSoft },
        labelStyle: { fontSize: 16, fill: t.ink },
      }]}
      yAxis={[{
        scaleType: "linear",
        min: axMin,
        max: axMax,
        label: "Sample Quantiles",
        tickLabelStyle: { fontSize: 14, fill: t.inkSoft },
        labelStyle: { fontSize: 16, fill: t.ink },
      }]}
    >
      <ChartsGrid horizontal vertical />
      <ReferenceLine />
      <ScatterPlot skipAnimation />
      <ChartsXAxis />
      <ChartsYAxis />
      {/* L-frame: mask top and right spines with background color */}
      <line
        x1={MARGIN.left - 1} y1={MARGIN.top}
        x2={width - MARGIN.right + 1} y2={MARGIN.top}
        stroke={t.pageBg}
        strokeWidth={3}
      />
      <line
        x1={width - MARGIN.right} y1={MARGIN.top - 1}
        x2={width - MARGIN.right} y2={height - MARGIN.bottom + 1}
        stroke={t.pageBg}
        strokeWidth={3}
      />
      {/* Title */}
      <text
        x={width / 2}
        y={36}
        textAnchor="middle"
        fontSize={22}
        fontWeight={600}
        fill={t.ink}
      >
        {TITLE}
      </text>
      {/* Subtitle */}
      <text
        x={width / 2}
        y={58}
        textAnchor="middle"
        fontSize={14}
        fill={t.inkSoft}
      >
        Order fulfillment time (n = 60, standardized) vs. Normal — upper-tail curvature reveals right skew
      </text>
      {/* Reference-line caption */}
      <text
        x={width - MARGIN.right - 14}
        y={MARGIN.top + 28}
        textAnchor="end"
        fontSize={14}
        fill={t.inkSoft}
      >
        {"– – –  y = x  (perfect Normal fit)"}
      </text>
    </ChartContainer>
  );
}

Retrieve this implementation

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

Part of Basic Q-Q Plot on anyplot.ai.

Other implementations