import React, { useEffect, useMemo, useRef, useState } from 'react'; import { createRoot } from 'react-dom/client'; import { VIEW, RINGS, LABEL_STEPS, assertLayout, box, hits, inside, roundedHull } from './layout.js'; import './style.css'; const LOGO_ROOT = 'wvs/'; function visibleFamilyNames(groups, hidden) { return Object.keys(groups).filter(family => !hidden.has(family)); } function Tooltip({ active, geometry, id = 'model-tooltip', panel }) { if (!active) return null; const left = active.tooltipLeft ?? `${Math.min(86, geometry.x(active.x) / VIEW.width * 100)}%`; const top = active.tooltipTop ?? `${Math.min(82, geometry.y(active.y) / VIEW.height * 100)}%`; const release = active.provenance.release_created && release {active.provenance.release_created}; return ; } function ModelMarker({ model, placement, geometry, setActive, clearActive, markerRef, logo }) { const cx = geometry.x(model.x), cy = geometry.y(model.y); const label = model.label ? placement[`model:${model.name}`] : null; const leader = label && Math.hypot(label.cx - cx, label.cy - cy) > 20; return setActive(model)} onPointerLeave={clearActive} onFocus={() => setActive(model)} onBlur={clearActive}> {leader && } {label && {model.label}} ; } function ordinaryLeastSquares(models, xValue, yValue) { const x = models.map(xValue); const y = models.map(yValue); const uniqueX = new Set(x); if (uniqueX.size < 2) return null; const xMean = x.reduce((sum, value) => sum + value, 0) / x.length; const yMean = y.reduce((sum, value) => sum + value, 0) / y.length; const slope = x.reduce((sum, value, index) => sum + (value - xMean) * (y[index] - yMean), 0) / x.reduce((sum, value) => sum + (value - xMean) ** 2, 0); const intercept = yMean - slope * xMean; const residual = y.reduce((sum, value, index) => sum + (value - (intercept + slope * x[index])) ** 2, 0); const total = y.reduce((sum, value) => sum + (value - yMean) ** 2, 0); return { slope, intercept, n: models.length, r2: total === 0 ? null : 1 - residual / total }; } function ReleaseScatter({ data, hidden, field, title, axisMode }) { const dated = useMemo(() => data.models.filter(model => Number.isFinite(Date.parse(model.provenance.release_created ?? ''))) .toSorted((a, b) => a.provenance.release_created.localeCompare(b.provenance.release_created) || a.name.localeCompare(b.name)), [data]); const matched = useMemo(() => data.models.filter(model => Number.isFinite(model.provenance.hle_score)) .toSorted((a, b) => a.provenance.hle_score - b.provenance.hle_score || a.name.localeCompare(b.name)), [data]); const plotted = axisMode === 'release-date' ? dated : matched; const visible = plotted.filter(model => !hidden.has(model.family)); const coordinate = model => field === 'x' ? -model.x : model.y; const xValue = model => axisMode === 'release-date' ? Date.parse(model.provenance.release_created) : model.provenance.hle_score; const fit = ordinaryLeastSquares(visible, xValue, coordinate); const [active, setActive] = useState(null); const width = 1200, height = 320, left = 96, right = 35, top = 42, bottom = 48; const xValues = plotted.map(xValue); const values = plotted.map(coordinate); const minX = Math.min(...xValues), maxX = Math.max(...xValues); const minValue = Math.min(...values), maxValue = Math.max(...values); const plotX = value => left + (value - minX) / (maxX - minX || 1) * (width - left - right); const valueY = value => top + (maxValue - value) / (maxValue - minValue || 1) * (height - top - bottom); const panelId = `release-${field}`; const tooltipId = `${panelId}-tooltip`; const yDirection = field === 'y' ? data.axis.y : [...data.axis.x].reverse(); const visibleX = visible.map(xValue); const fitEnd = fit && [Math.min(...visibleX), Math.max(...visibleX)].map(value => [plotX(value), valueY(fit.intercept + fit.slope * value)]); const frontier = useMemo(() => { if (axisMode !== 'release-date') return []; let high = -Infinity; return visible.filter(model => Number.isFinite(model.provenance.hle_score)).filter(model => { if (model.provenance.hle_score <= high) return false; high = model.provenance.hle_score; return true; }); }, [axisMode, visible]); const frontierPlacement = useMemo(() => { const bounds = { left, right: width - right, top, bottom: height - bottom }; const taken = [ ...plotted.map(model => box(plotX(xValue(model)), valueY(coordinate(model)), 16, 16)), ...(fit ? [box(left + 66, top + 9, 128, 18)] : []), ]; const placement = {}; for (const model of frontier) { const anchor = { x: plotX(xValue(model)), y: valueY(coordinate(model)) }; const size = { width: model.name.length * 8 + 12, height: 18 }; let found = null; for (const step of LABEL_STEPS) { for (const [dx, dy] of RINGS) { const candidate = box(anchor.x + dx * (step + size.width / 3), anchor.y + dy * step, size.width, size.height); if (inside(candidate, bounds) && !taken.some(obstacle => hits(candidate, obstacle))) { found = candidate; break; } } if (found) break; } if (!found) { for (let cy = bounds.top + size.height / 2; cy <= bounds.bottom - size.height / 2 && !found; cy += size.height + 4) { for (let cx = bounds.left + size.width / 2; cx <= bounds.right - size.width / 2; cx += 12) { const candidate = box(cx, cy, size.width, size.height); if (!taken.some(obstacle => hits(candidate, obstacle))) { found = candidate; break; } } } } if (!found) found = box(anchor.x, Math.max(bounds.top + size.height / 2, anchor.y - size.height), size.width, size.height); placement[model.name] = { ...found, anchor }; taken.push(found); } return placement; }, [frontier, plotted, minX, maxX, minValue, maxValue]); const activate = (model, event) => { const box = event.currentTarget.closest('.scatter-shell').getBoundingClientRect(); const point = event.currentTarget.getBoundingClientRect(); setActive({ ...model, tooltipValue: coordinate(model), tooltipScore: axisMode === 'capability' ? model.provenance.hle_score : null, tooltipLeft: `${Math.min(82, (point.left - box.left) / box.width * 100)}%`, tooltipTop: `${Math.min(78, (point.top - box.top) / box.height * 100)}%` }); }; return

{title}

{title} Scatter plot with {axisMode === 'release-date' ? 'release date' : 'HLE score'} on the horizontal axis and {yDirection.join(' to ')} increasing upward on the vertical axis. {visible.length} of {plotted.length} matched models are visible from {visibleFamilyNames(Object.groupBy(data.models, model => model.family), hidden).join(', ') || 'no families'}; {data.models.length - plotted.length} plotted models lack this x value and are omitted only here. Each white-ring logo mark is a model. {fit ? `The thin line is an ordinary least squares descriptive fit to the ${fit.n} currently visible matched models${fit.r2 === null ? '; R squared is unavailable because the y values are constant' : `; R squared is ${fit.r2.toFixed(2)}`}.` : 'The fit is hidden because fewer than two distinct x values are visible.'} Hover or keyboard focus a mark for model-specific details. {Array.from({ length: 5 }, (_, index) => )} {yDirection.join(' -> ')} {'↑'} {axisMode === 'release-date' ? new Date(minX).toISOString().slice(0, 10) : minX.toFixed(1)} {axisMode === 'release-date' ? new Date(maxX).toISOString().slice(0, 10) : maxX.toFixed(1)} {maxValue.toFixed(2)} {minValue.toFixed(2)} {fitEnd && OLS, n={fit.n}, R² {fit.r2 === null ? 'unavailable' : fit.r2.toFixed(2)}} {!fit && Fit unavailable: fewer than two release dates} {plotted.map(model => activate(model, event)} onPointerLeave={() => setActive(null)} onFocus={event => activate(model, event)} onBlur={() => setActive(null)}> )} {axisMode === 'release-date' && frontier.map(model => {model.name})}
; } function ReleaseScatters({ data, hidden, axisMode, setAxisMode }) { const xLabel = axisMode === 'release-date' ? 'Release date' : data.capability_x.label; return
{axisMode === 'capability' && Artificial Analysis HLE score, saved {data.capability_x.fetched_utc.slice(0, 10)}. {data.capability_x.matched_models} matched, {data.models.length - data.capability_x.matched_models} omitted.}
; } function Map({ data }) { const query = new URLSearchParams(location.search); const [hidden, setHidden] = useState(() => new Set(query.get('hide')?.split(',').filter(Boolean))); const [axisMode, setAxisMode] = useState(() => query.get('axis') === 'capability' ? 'capability' : 'release-date'); const [active, setActive] = useState(() => data.models.find(model => model.name === (query.get('tooltip') || query.get('focus'))) ?? null); const focusName = query.get('focus'); const focusRef = useRef(null); const groups = useMemo(() => Object.groupBy(data.models, model => model.family), [data]); const { labels, geometry } = useMemo(() => assertLayout(data), [data]); const visibleFamilies = visibleFamilyNames(groups, hidden); const visibleModelCount = data.models.filter(model => !hidden.has(model.family)).length; useEffect(() => { if (focusName) focusRef.current?.focus(); }, [focusName]); const clearActive = event => { if (event.currentTarget.matches(':focus')) return; setActive(null); }; const toggle = family => setHidden(old => { const next = new Set(old); if (next.has(family)) next.delete(family); else next.add(family); return next; }); const xMedian = geometry.x(data.median.x), yMedian = geometry.y(data.median.y); return <>
{Object.entries(groups).map(([family]) => { const visible = !hidden.has(family); return ; })}
Moral Maps: Where Do Frontier Models' Cultural Values Lie? World Values Survey cultural map. Horizontal direction runs from Self-expression on the left to Survival on the right. Vertical direction runs from Traditional below to Secular-Rational above. Coloured dots are selected WVS countries, outlines are cultural regions, and white-ring logo marks are models. {visibleModelCount} model marks are visible from {visibleFamilies.join(', ') || 'no families'}. Use the family controls to hide marks and labels. Hover or keyboard focus a model for model-specific details. {Array.from({ length: 8 }, (_, index) => )}{Array.from({ length: 6 }, (_, index) => )} {data.zone_hulls.map(zone => )} {data.countries.map(country => {country.label && {country.name}})} {data.zone_hulls.map(zone => {zone.name})} {Object.entries(groups).map(([family, models]) => {models.map(model => )})} {data.axis.y[1]}{data.axis.y[0]}{data.axis.x[0]}{data.axis.x[1]} {data.title.split('\n').map((line, index) => {line})} {data.note.split('\n').map((line, index) => {line})}

This uses the World Values Survey, which social scientists designed to compare human cultures. My theory is that a survey broad enough to cover human cultures might also capture LLMs, which are potentially outside human culture.

This updates The Economist’s earlier plot, which found that “the models are often more extreme than the average respondent in every country included in the polling.”