result_data_ojs = transpose(result_data)
Plot.plot({
color: {
legend: false,
type: "pow",
range: theme.cellRange
},
y: { grid: false, label: "Home" },
x: { grid: false, label: "Away", axis: "top" },
marks: [
Plot.cell(result_data_ojs, {
x: "away_ft_goals",
y: "home_ft_goals",
fill: "frac",
tip: true,
title: d => d.home_ft_goals + " : " + d.away_ft_goals + "\n\n" + d.frac?.toFixed(3) + "%"
}),
Plot.text(result_data_ojs, {
x: "away_ft_goals",
y: "home_ft_goals",
text: d => d.frac?.toFixed(3) + "%",
fill: "black"
})
],
style: { background: theme.bg, fill: "#D3D3D3", fontFamily: "Poppins", fontSize: "20px" },
height: chartWidth,
width: chartWidth,
marginLeft: 50, marginRight: 50, marginTop: 50, marginBottom: 50,
insetTop: 0, insetBottom: 0, insetLeft: 0, insetRight: 0
})Result Statistics (F)
Stats from all matches played world-wide since 2000
// Shared OJS theme — reads the CSS custom properties defined in styles.css :root
// so chart colors have a single source of truth (no copy-pasted hexes).
theme = {
const root = getComputedStyle(document.documentElement);
const v = (name, fallback) => (root.getPropertyValue(name).trim() || fallback);
return {
bg: v("--soccerverse-bg", "#383A3F"),
fg: v("--soccerverse-fg", "#F6B352"),
hl: v("--soccerverse-hl", "#F68657"),
// light→accent ramp used by the result-matrix heatmaps
cellRange: ["#fef3e4", v("--soccerverse-fg", "#F6B352")]
};
}// Responsive chart width: fills the content column (capped at 950) and
// updates on window resize so charts never overflow on small screens.
chartWidth = Generators.observe((notify) => {
const measure = () =>
notify(Math.min(950, (document.querySelector("main")?.clientWidth ?? 950) - 40));
measure();
addEventListener("resize", measure);
return () => removeEventListener("resize", measure);
})Result Matrix
Average goals
goal_data_ojs = transpose(goal_data)
Plot.plot({
color: { legend: true },
y: { grid: false, domain: d3.extent(goal_data_ojs, d => d.avg_goals), label: "↑ Avg. Goals", ticks: 5 },
x: { grid: false, domain: d3.extent(goal_data_ojs, d => d.year), label: "Year", ticks: 5 },
marks: [
Plot.gridY({ ticks: 5, stroke: "#fff", strokeOpacity: 0.2 }),
Plot.dot(goal_data_ojs, { x: "year", y: "avg_goals", fill: "continent", tip: true, title: (d) => `${d.avg_goals} (${d.continent})` }),
Plot.line(goal_data_ojs, { x: "year", y: "avg_goals", z: "continent", stroke: "continent" })
],
style: { background: theme.bg, fill: "#D3D3D3", fontFamily: "Poppins", fontSize: "20px" },
height: 600,
width: chartWidth,
marginLeft: 50, marginRight: 50, marginTop: 50, marginBottom: 50,
insetTop: 0, insetBottom: 0, insetLeft: 0, insetRight: 0
})Home field advantage
Home field advantage per year and continent
hfa_data_ojs = transpose(hfa_data)
Plot.plot({
color: { legend: true },
y: { grid: false, domain: d3.extent(hfa_data_ojs, d => d.hfa), label: "↑ Fraction of home games won", ticks: 5 },
x: { grid: false, domain: d3.extent(hfa_data_ojs, d => d.year), label: "Year", ticks: 5 },
marks: [
Plot.gridY({ ticks: 5, stroke: "#fff", strokeOpacity: 0.2 }),
Plot.dot(hfa_data_ojs, { x: "year", y: "hfa", fill: "continent", tip: true, title: (d) => `${d.hfa} (${d.continent})` }),
Plot.line(hfa_data_ojs, { x: "year", y: "hfa", z: "continent", stroke: "continent" })
],
style: { background: theme.bg, fill: "#D3D3D3", fontFamily: "Poppins", fontSize: "20px" },
height: 600,
width: chartWidth,
marginLeft: 50, marginRight: 50, marginTop: 50, marginBottom: 50,
insetTop: 0, insetBottom: 0, insetLeft: 0, insetRight: 0
})(Last update: 2026-06-30)