A scatter plot with a LOWESS (Locally Weighted Scatterplot Smoothing) regression curve overlaid. LOWESS is a non-parametric method that fits smooth curves by performing local weighted regressions at each point, adapting to local data patterns without assuming a specific functional form. This makes it ideal for exploring complex relationships where the underlying pattern is unknown or varies across the data range.

// anyplot.ai
// scatter-regression-lowess: Scatter Plot with LOWESS Regression
// Library: chartjs 4.4.7 | JavaScript 22.23.2
// Quality: 91/100 | Created: 2026-09-09
const t = window.ANYPLOT_TOKENS;
// --- Data (in-memory, deterministic) ----------------------------------------
// Weekly ad-spend campaigns and their conversion rate: response rises with
// spend, plateaus, then dips slightly at very high spend (ad fatigue) — a
// non-linear pattern with no obvious closed-form model, well suited to LOWESS.
function makeRng(seed) {
let state = seed >>> 0;
return () => {
state = (1664525 * state + 1013904223) >>> 0;
return state / 4294967296;
};
}
const rng = makeRng(42);
function gaussian() {
const u1 = Math.max(rng(), 1e-9);
const u2 = rng();
return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
}
const n = 150;
const adSpend = [];
for (let i = 0; i < n; i++) {
adSpend.push(1 + (99 * i) / (n - 1) + (rng() - 0.5) * 0.6);
}
adSpend.sort((a, b) => a - b);
const conversionRate = adSpend.map((x) => {
const rise = 8 * (1 - Math.exp(-x / 20));
const fatigue = x > 70 ? 0.015 * Math.pow(x - 70, 1.5) : 0;
return 2 + rise - fatigue + gaussian() * 0.9;
});
// --- LOWESS (locally weighted regression with tricube weights + two
// bisquare robustness iterations, following Cleveland 1979) -------------
function lowess(xs, ys, frac, iterations) {
const count = xs.length;
const windowSize = Math.max(2, Math.round(frac * count));
let robustWeights = new Array(count).fill(1);
let fitted = new Array(count).fill(0);
for (let iter = 0; iter <= iterations; iter++) {
for (let i = 0; i < count; i++) {
const xi = xs[i];
const distances = xs.map((x) => Math.abs(x - xi));
const bandwidth = [...distances].sort((a, b) => a - b)[windowSize - 1] || 1e-9;
let sumW = 0;
let sumWX = 0;
let sumWY = 0;
let sumWXX = 0;
let sumWXY = 0;
for (let j = 0; j < count; j++) {
const d = distances[j] / bandwidth;
if (d >= 1) continue;
const w = Math.pow(1 - Math.pow(d, 3), 3) * robustWeights[j];
sumW += w;
sumWX += w * xs[j];
sumWY += w * ys[j];
sumWXX += w * xs[j] * xs[j];
sumWXY += w * xs[j] * ys[j];
}
const denom = sumW * sumWXX - sumWX * sumWX;
let slope = 0;
let intercept = sumWY / sumW;
if (Math.abs(denom) > 1e-9) {
slope = (sumW * sumWXY - sumWX * sumWY) / denom;
intercept = (sumWY - slope * sumWX) / sumW;
}
fitted[i] = intercept + slope * xi;
}
if (iter < iterations) {
const absResiduals = ys.map((y, i) => Math.abs(y - fitted[i]));
const sortedAbs = [...absResiduals].sort((a, b) => a - b);
const mid = Math.floor(count / 2);
const mad = count % 2 !== 0 ? sortedAbs[mid] : (sortedAbs[mid - 1] + sortedAbs[mid]) / 2;
const scale = 6 * mad || 1e-9;
robustWeights = ys.map((y, i) => {
const u = (y - fitted[i]) / scale;
return Math.abs(u) < 1 ? Math.pow(1 - u * u, 2) : 0;
});
}
}
return fitted;
}
const lowessFit = lowess(adSpend, conversionRate, 0.35, 2);
const curvePoints = adSpend.map((x, i) => ({ x, y: lowessFit[i] }));
// --- Helpers -----------------------------------------------------------------
function withAlpha(hex, alpha) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
const TITLE_TEXT =
"Ad Spend vs. Conversion Rate · scatter-regression-lowess · javascript · chartjs · anyplot.ai";
const TITLE_FONT_SIZE = Math.max(15, Math.round(22 * Math.min(1, 67 / TITLE_TEXT.length)));
// --- Mount -------------------------------------------------------------------
const canvas = document.createElement("canvas");
document.getElementById("container").appendChild(canvas);
// --- Chart ---------------------------------------------------------------
new Chart(canvas, {
type: "scatter",
data: {
datasets: [
{
type: "scatter",
label: "Weekly campaigns",
data: adSpend.map((x, i) => ({ x, y: conversionRate[i] })),
backgroundColor: withAlpha(t.palette[0], 0.6),
borderColor: t.pageBg,
borderWidth: 1,
pointRadius: 5,
pointHoverRadius: 6,
},
{
type: "line",
label: "LOWESS fit (frac = 0.35)",
data: curvePoints,
borderColor: t.palette[1],
backgroundColor: t.palette[1],
borderWidth: 3.5,
pointRadius: 0,
fill: false,
tension: 0,
},
],
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: false,
plugins: {
title: {
display: true,
text: TITLE_TEXT,
color: t.ink,
font: { size: TITLE_FONT_SIZE, weight: "500" },
padding: { bottom: 20 },
},
legend: {
position: "top",
align: "end",
labels: { color: t.ink, font: { size: 16 }, boxWidth: 24, usePointStyle: true },
},
},
scales: {
x: {
type: "linear",
ticks: { color: t.inkSoft, font: { size: 14 } },
grid: { color: t.grid },
title: { display: true, text: "Weekly Ad Spend ($1,000s)", color: t.ink, font: { size: 18 } },
},
y: {
ticks: { color: t.inkSoft, font: { size: 14 } },
grid: { color: t.grid },
title: { display: true, text: "Conversion Rate (%)", color: t.ink, font: { size: 18 } },
},
},
},
});
Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/scatter-regression-lowess/chartjs/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": "scatter-regression-lowess",
"language": "javascript",
"library": "chartjs",
"page": "https://anyplot.ai/scatter-regression-lowess/javascript/chartjs",
"hub": "https://anyplot.ai/scatter-regression-lowess",
"code_json": "https://api.anyplot.ai/specs/scatter-regression-lowess/chartjs/code",
"spec_json": "https://api.anyplot.ai/specs/scatter-regression-lowess",
"render_light_png": "https://storage.googleapis.com/anyplot-images/plots/scatter-regression-lowess/javascript/chartjs/plot-light.png",
"render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/scatter-regression-lowess/javascript/chartjs/plot-dark.png",
"interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/scatter-regression-lowess/javascript/chartjs/plot-light.html",
"interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/scatter-regression-lowess/javascript/chartjs/plot-dark.html",
"quality_score": 91.0,
"license": "MIT",
"guide": "https://anyplot.ai/llms.txt"
}Part of Scatter Plot with LOWESS Regression on anyplot.ai.