Files
moral-maps/docs/wvs/index.html
T
2026-09-17 05:06:36 +08:00

43 lines
5.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Frontier LLMs on the World Values Survey</title>
<style>
body { margin: 0; background: #faf8f2; color: #292524; font: 16px system-ui, sans-serif; }
main { max-width: 1100px; margin: auto; padding: 1rem; } h1 { margin: .2rem 0; } p { max-width: 78ch; }
#controls { display: flex; flex-wrap: wrap; gap: .4rem; margin: 1rem 0; }
button { border: 2px solid var(--color); border-radius: 1rem; padding: .3rem .7rem; background: var(--color); color: white; cursor: pointer; font-weight: 600; }
button.off { background: white; color: #292524; opacity: .5; text-decoration: line-through; }
svg { width: 100%; height: auto; background: #faf8f2; border: 1px solid #292524; }
.zone { fill: none; stroke: #94a3b8; stroke-width: 2; stroke-dasharray: 5 3; } .country { fill: #64748b; stroke: white; stroke-width: .6; }
.star { stroke: white; stroke-width: 1; } .zone-label { font-weight: 700; font-style: italic; fill: #475569; }
</style>
<main>
<h1>Frontier LLMs on the World Values Survey</h1>
<p>Rated sampling, 12 items x 12 samples for new points. Filled chips are visible; an outlined, struck-through chip hides that familys stars. Each chip carries the familys latest-release name. Grey dots are WVS countries, coloured stars are models, and dashed hulls are cultural regions. Coordinates, labels, family membership and colours come from <code>wvs_map_data.json</code>, the generated artifact shared with the static map.</p>
<div id="controls" aria-label="Model family visibility"></div>
<svg id="map" viewBox="0 0 1000 720" role="img" aria-label="WVS map of model and country coordinates"></svg>
<p>Historical coordinates are recovered rounded display values. Coder and vision-language Qwen variants are visible but are specialized variants, not direct-instruct size-trend evidence. See the repository CI table and request ledger for sample and protocol evidence.</p>
</main>
<script>
const NS = 'http://www.w3.org/2000/svg';
const el = (tag, attrs = {}, text) => { const node = document.createElementNS(NS, tag); for (const [key, value] of Object.entries(attrs)) node.setAttribute(key, value); if (text) node.textContent = text; return node; };
const hull = pts => { const p = [...pts].sort((a,b) => a.x-b.x || a.y-b.y); const cross = (o,a,b) => (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x); const half = a => { const out=[]; for (const x of a) { while (out.length > 1 && cross(out.at(-2),out.at(-1),x) <= 0) out.pop(); out.push(x); } return out; }; return [...half(p), ...half([...p].reverse()).slice(1,-1)]; };
fetch('wvs_map_data.json').then(r => { if (!r.ok) throw new Error(`data: ${r.status}`); return r.json(); }).then(data => {
const svg = document.querySelector('#map'), margin = 70, all = [...data.countries, ...data.models];
const minX=Math.min(...all.map(p=>p.x)), maxX=Math.max(...all.map(p=>p.x)), minY=Math.min(...all.map(p=>p.y)), maxY=Math.max(...all.map(p=>p.y));
const x=v=>margin+(v-minX)/(maxX-minX)*(1000-2*margin), y=v=>720-margin-(v-minY)/(maxY-minY)*(720-2*margin);
svg.append(el('line',{x1:margin,y1:y((minY+maxY)/2),x2:1000-margin,y2:y((minY+maxY)/2),class:'axis'})); svg.append(el('line',{x1:x((minX+maxX)/2),y1:margin,x2:x((minX+maxX)/2),y2:720-margin,class:'axis'}));
for (const [name,members] of Object.entries(data.zones)) { const points=data.countries.filter(p=>members.includes(p.name)); if(points.length<3) continue; const h=hull(points); svg.append(el('polygon',{points:h.map(p=>`${x(p.x)},${y(p.y)}`).join(' '),class:'zone'})); const c=h.reduce((sum,p)=>({x:sum.x+p.x/h.length,y:sum.y+p.y/h.length}),{x:0,y:0}); svg.append(el('text',{x:x(c.x),y:y(c.y),class:'zone-label','text-anchor':'middle'},name)); }
for (const p of data.countries) { const c=el('circle',{cx:x(p.x),cy:y(p.y),r:3,class:'country'}); c.append(el('title',{},p.name)); svg.append(c); }
const groups=new Map(); for(const p of data.models) groups.set(p.family,[...(groups.get(p.family)||[]),p]); const hidden=new Set(new URLSearchParams(location.search).get('hide')?.split(',').filter(Boolean));
for (const [family,points] of groups) { const group=el('g',{'data-family':family}); for(const p of points) { const star=el('path',{d:`M ${x(p.x)} ${y(p.y)-8} L ${x(p.x)+2.4} ${y(p.y)-2.4} L ${x(p.x)+8} ${y(p.y)-2.4} L ${x(p.x)+3.6} ${y(p.y)+1.6} L ${x(p.x)+5.2} ${y(p.y)+7} L ${x(p.x)} ${y(p.y)+4} L ${x(p.x)-5.2} ${y(p.y)+7} L ${x(p.x)-3.6} ${y(p.y)+1.6} L ${x(p.x)-8} ${y(p.y)-2.4} L ${x(p.x)-2.4} ${y(p.y)-2.4} Z`,fill:p.color,class:'star'}); star.append(el('title',{},p.name)); group.append(star); } svg.append(group);
const latest=points.find(p=>p.label)?.label ?? points[0].name, button=document.createElement('button'); button.textContent=`${family}: ${latest}`; button.style.setProperty('--color',points[0].color);
const labelOffsets={grok:{dx:-72,dy:-30},muse:{dx:12,dy:-11},mistral:{dx:-96,dy:-26},llama:{dx:-96,dy:12}};
for (const p of points.filter(p=>p.label)) { const {dx,dy}=labelOffsets[family]??{dx:11,dy:-11}; group.append(el('line',{x1:x(p.x),y1:y(p.y),x2:x(p.x)+dx*.82,y2:y(p.y)+dy*.82,stroke:p.color,'stroke-width':1})); group.append(el('text',{x:x(p.x)+dx,y:y(p.y)+dy,fill:p.color,'font-size':10,'font-weight':700},p.label)); }
let visible = !hidden.has(family); const setVisible=on=>{visible=on;group.setAttribute('display',on?'inline':'none');button.classList.toggle('off',!on);button.setAttribute('aria-pressed',String(on));}; setVisible(visible); button.onclick=()=>setVisible(!visible); document.querySelector('#controls').append(button);
}
for(const [text,px,py,anchor] of [[data.axis.x[0],margin,690,'start'],[data.axis.x[1],1000-margin,690,'end'],[data.axis.y[0],500,700,'middle'],[data.axis.y[1],500,28,'middle']]) svg.append(el('text',{x:px,y:py,'text-anchor':anchor,fill:'#57534e','font-weight':700},text));
}).catch(error=>{document.body.append(`Could not load map data: ${error.message}`);});
</script>