tornadomap / index.html
snesbitt's picture
Annual tornado density browser, 1950-2026
3ac20c1
Raw
History Blame Contribute Delete
44.2 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Seventy-seven Years of U.S. Tornadoes</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@600;700;800&family=Source+Sans+3:wght@300;400;600;700&display=swap" rel="stylesheet">
<style>
:root{
--blue:#13294B; --orange:#FF5F05; --cyan:#22D3EE;
--ink:#13294B; --muted:#6B7684; --hair:#E3E6EA; --paper:#FFFFFF;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0;background:var(--paper);color:var(--ink);
font-family:'Source Sans 3',-apple-system,Segoe UI,Helvetica,Arial,sans-serif;
-webkit-font-smoothing:antialiased;}
.wrap{max-width:1060px;margin:0 auto;padding:0 28px 72px;}
header{display:flex;align-items:center;gap:14px;padding:26px 0 18px;}
header img{width:38px;height:38px;border-radius:5px;display:block}
header .dept{font-size:14px;font-weight:600;letter-spacing:.01em}
header .sub{font-size:12px;color:var(--muted);margin-top:1px}
.rule{height:1px;background:var(--hair);}
.rule.accent{height:3px;background:var(--orange);width:64px;margin:26px 0 0}
h1{font-family:'Montserrat',sans-serif;font-weight:800;font-size:40px;line-height:1.08;
letter-spacing:-.018em;margin:20px 0 0;max-width:19ch}
.deck{font-size:18px;line-height:1.55;color:#41505F;max-width:62ch;margin:16px 0 0;font-weight:300}
.stage{display:grid;grid-template-columns:1fr 238px;gap:24px;align-items:start;margin:34px 0 0;}
.mapwrap{min-width:0;position:relative;touch-action:pan-y}
canvas#map{width:100%;display:block;background:#fff}
.card{background:var(--blue);color:#fff;
border-radius:8px;padding:15px 18px 14px;min-width:212px;
box-shadow:0 4px 16px rgba(19,41,75,.13)}
.card .yr{font-family:'Montserrat',sans-serif;font-weight:800;font-size:31px;line-height:1;
letter-spacing:-.01em}
.card .partial{display:inline-block;margin-top:7px;font-size:10.5px;font-weight:700;
letter-spacing:.07em;text-transform:uppercase;color:#0B1B33;background:var(--cyan);
padding:3px 7px;border-radius:3px}
.card hr{border:0;height:1px;background:rgba(255,255,255,.17);margin:13px 0}
.card .lbl{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:#93A4BE;
font-weight:600}
.card .val{font-family:'Montserrat',sans-serif;font-weight:700;font-size:23px;
color:var(--cyan);line-height:1.15;margin-top:2px}
.card .val small{font-family:'Source Sans 3';font-weight:400;font-size:12px;color:#93A4BE;
letter-spacing:0}
.card .row+.row{margin-top:11px}
.card .towns{font-size:12.5px;color:#E6ECF5;margin-top:5px;font-weight:600;line-height:1.3}
.card .where{font-size:11px;color:#7F92AE;margin-top:2px;letter-spacing:.01em}
.legend{display:flex;align-items:center;flex-wrap:wrap;gap:8px 12px;margin:16px 0 0;
font-size:12px;color:var(--muted)}
.legend>span{white-space:nowrap}
.ramp{height:9px;width:190px;border-radius:2px}
.dot{width:5px;height:5px;background:#111;border-radius:50%;display:inline-block;
vertical-align:middle;margin-right:5px}
.controls{display:flex;align-items:center;gap:16px;margin:30px 0 0}
button.play{border:1px solid var(--hair);background:#fff;color:var(--blue);cursor:pointer;
width:40px;height:40px;border-radius:50%;font-size:14px;line-height:1;display:grid;
place-items:center;transition:background .15s,border-color .15s}
button.play:hover{background:#F6F8FA;border-color:#C9D0D8}
.scrub{flex:1}
canvas#tl{width:100%;height:64px;display:block;cursor:pointer;touch-action:none}
.tlnote{display:flex;justify-content:space-between;font-size:11.5px;color:var(--muted);
margin-top:2px}
.notes{margin:34px 0 0;font-size:13.5px;line-height:1.6;color:#5A6774;max-width:74ch;
font-weight:300}
.notes b{font-weight:600;color:var(--ink)}
footer{margin:30px 0 0;padding-top:16px;border-top:1px solid var(--hair);
font-size:12px;color:var(--muted);line-height:1.6}
.loading{position:absolute;inset:0;display:grid;place-items:center;color:var(--muted);
font-size:13px;background:#fff}
.loading.gone{display:none}
@media (max-width:720px){
.legend{font-size:11.5px}
button.play{width:46px;height:46px}
canvas#tl{height:72px}
h1{font-size:29px}
.stage{grid-template-columns:1fr;gap:14px} .card{min-width:0}
.wrap{padding:0 18px 48px}
}
</style>
</head>
<body>
<div class="wrap">
<header>
<img src="data:image/jpeg;base64,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" alt="CliMAS"/>
<div>
<div class="dept">Department of Climate, Meteorology &amp; Atmospheric Sciences</div>
<div class="sub">University of Illinois Urbana-Champaign</div>
</div>
</header>
<div class="rule"></div>
<h1>Seventy-seven years of U.S. tornadoes</h1>
<div class="rule accent"></div>
<p class="deck">Every tornado recorded in the contiguous United States since 1950, placed at the
midpoint of its track and smoothed into a density field. Drag the timeline, scroll, or use the
arrow keys to move through the years.</p>
<div class="stage">
<div class="mapwrap"><canvas id="map"></canvas>
<div class="loading" id="load">Loading 77 years of tornado data…</div></div>
<div class="card">
<div class="yr" id="cy">2026</div>
<div id="cp"><span class="partial">Through 14 August</span></div>
<hr/>
<div class="row">
<div class="lbl">Peak density</div>
<div class="val" id="cmax">12.62 <small>per 10,000 km²</small></div>
<div class="towns" id="ctowns"></div>
<div class="where" id="cwhere"></div>
</div>
<div class="row">
<div class="lbl">Rank, all 77 years</div>
<div class="val" id="crank">#1</div>
</div>
<div class="row">
<div class="lbl">Tornadoes &mdash; rank</div>
<div class="val" id="ccount">977 <small id="ccrank"></small></div>
</div>
<div class="row">
<div class="lbl">Most tornadoes</div>
<div class="val" id="cstate"></div>
<div class="where" id="cstaten"></div>
</div>
</div>
</div>
<div class="legend">
<span>Density</span><canvas class="ramp" id="ramp" width="190" height="9"></canvas>
<span id="rampmax"></span>
<span style="margin-left:14px"><span class="dot"></span>individual tornado</span>
<span style="margin-left:14px"><span style="display:inline-block;width:11px;height:11px;
border:2.5px solid #FF2D95;vertical-align:-2px;margin-right:5px"></span>peak 80&nbsp;km cell</span>
<span style="margin-left:14px"><span style="display:inline-block;width:14px;height:0;
border-top:2px solid #13294B;vertical-align:middle;margin-right:5px"></span>state with the most tornadoes</span>
</div>
<div class="controls">
<button class="play" id="play" aria-label="Play">&#9654;</button>
<div class="scrub">
<canvas id="tl"></canvas>
<div class="tlnote"><span>1950</span><span id="tlmid">peak density each year</span><span>2026</span></div>
</div>
</div>
<p class="notes">
<b>Reading this map.</b> Color shows how tightly tornadoes were packed — tornadoes per
10,000 km² per year, on an 80 km grid smoothed with a 120 km Gaussian kernel, after
Brooks, Doswell &amp; Kay (2003). Black dots are individual tornadoes. The color scale is
fixed across all years, so a pale map is a genuinely quiet year.
<br/><br/>
<b>Reports are not a fixed yardstick.</b> How many tornadoes get recorded depends on
population density, observing technology and public awareness, none of which has held
steady since 1950. Early years are undercounted, particularly for weak tornadoes.
<br/><br/>
<b>The pink box</b> marks the single 80&nbsp;km grid cell holding that year's peak density.
<b>The state outlined in blue</b> recorded the most tornadoes that year. The card gives both
ranks: peak density, and total tornado count.
<br/><br/>
<b>The stat card names the two largest towns</b> inside the 80 km cell that holds that
year's peak density — or the two nearest, where the cell has no town of 15,000 or more.
<br/><br/>
<b>2026 is incomplete and estimated.</b> It covers 1 January – 14 August only. Preliminary
reports were deduplicated (1,530 raw reports collapse to 977 distinct tornadoes) and then
rescaled to a final-count basis using the 2010–2025 relationship between deduplicated
preliminary reports and final counts — 0.815 nationally, 0.732 in Illinois. Counts marked
<i>est.</i> are that estimate, not a tally. Every other year is a complete
calendar year, so 2026's rank is a floor, not a final figure.
</p>
<footer>
Data: NOAA/NWS Storm Prediction Center — 1950–2025 final severe weather database; 2026
preliminary daily storm reports. Gridded density available as a Zarr store alongside this page.<br/>
Analysis: Prof. Steve Nesbitt, Department of Climate, Meteorology &amp; Atmospheric Sciences,
University of Illinois Urbana-Champaign,
<a href="https://snesbitt.github.io" style="color:#13294B">snesbitt.github.io</a>.
</footer>
</div>
<script>
const map=document.getElementById('map'), ctx=map.getContext('2d');
const tl=document.getElementById('tl'), tctx=tl.getContext('2d');
const STATE={AL:'Alabama',AZ:'Arizona',AR:'Arkansas',CA:'California',CO:'Colorado',
CT:'Connecticut',DE:'Delaware',FL:'Florida',GA:'Georgia',ID:'Idaho',IL:'Illinois',
IN:'Indiana',IA:'Iowa',KS:'Kansas',KY:'Kentucky',LA:'Louisiana',ME:'Maine',MD:'Maryland',
MA:'Massachusetts',MI:'Michigan',MN:'Minnesota',MS:'Mississippi',MO:'Missouri',MT:'Montana',
NE:'Nebraska',NV:'Nevada',NH:'New Hampshire',NJ:'New Jersey',NM:'New Mexico',NY:'New York',
NC:'North Carolina',ND:'North Dakota',OH:'Ohio',OK:'Oklahoma',OR:'Oregon',PA:'Pennsylvania',
RI:'Rhode Island',SC:'South Carolina',SD:'South Dakota',TN:'Tennessee',TX:'Texas',UT:'Utah',
VT:'Vermont',VA:'Virginia',WA:'Washington',WV:'West Virginia',WI:'Wisconsin',WY:'Wyoming',DC:'D.C.'};
let M=null, DENS=null, PTS=null, cur=76, playing=false, timer=null, DPR=Math.min(2,devicePixelRatio||1);
// Data ships as base64 text: Hugging Face's pre-receive hook rejects binary
// files with unrecognised extensions, and text needs no Git LFS.
function b64bytes(s){
const bin=atob(s.trim()), n=bin.length, out=new Uint8Array(n);
for(let i=0;i<n;i++) out[i]=bin.charCodeAt(i);
return out;
}
Promise.all([
fetch('meta.json').then(r=>r.json()),
fetch('density.b64').then(r=>r.text()),
fetch('points.b64').then(r=>r.text())
]).then(([meta,d,p])=>{
M=meta;
DENS=b64bytes(d);
const pb=b64bytes(p), dv=new DataView(pb.buffer,pb.byteOffset,pb.byteLength);
PTS=new Uint16Array(pb.byteLength/2);
for(let i=0;i<PTS.length;i++) PTS[i]=dv.getUint16(i*2,true); // little-endian, as written
document.getElementById('rampmax').textContent='0 – '+M.vmax.toFixed(1)+' per 10,000 km²';
drawRamp(); sizeAll(); setYear(M.years.length-1); bind();
document.getElementById('load').classList.add('gone');
}).catch(e=>{
document.getElementById('load').textContent='Error: '+(e&&e.message?e.message:e);
console.error(e);
});
function lutRGB(v){const L=M.lut,i=v*3;return [L[i],L[i+1],L[i+2]];}
function drawRamp(){
const c=document.getElementById('ramp'), g=c.getContext('2d');
const im=g.createImageData(190,9);
for(let x=0;x<190;x++){const [r,gg,b]=lutRGB(Math.round(x/189*255));
for(let y=0;y<9;y++){const o=(y*190+x)*4;im.data[o]=r;im.data[o+1]=gg;im.data[o+2]=b;im.data[o+3]=255;}}
g.putImageData(im,0,0);
}
function sizeAll(){
const w=document.querySelector('.mapwrap').clientWidth, h=Math.round(w*M.view_aspect);
map.width=w*DPR; map.height=h*DPR; map.style.height=h+'px';
const tw=tl.parentElement.clientWidth;
tl.width=tw*DPR; tl.height=64*DPR; tl.style.height='64px';
if(M) {render(); drawTL();}
}
function render(){
const W=map.width, H=map.height;
ctx.clearRect(0,0,W,H);
// density -> offscreen image at grid resolution, then smooth-scaled
const nx=M.nx, ny=M.ny, off=cur*nx*ny;
const oc=document.createElement('canvas'); oc.width=nx; oc.height=ny;
const octx=oc.getContext('2d'), im=octx.createImageData(nx,ny);
for(let j=0;j<ny;j++){
for(let i=0;i<nx;i++){
const v=DENS[off+j*nx+i];
const o=((ny-1-j)*nx+i)*4; // flip: grid row 0 is southernmost
if(v<=1){im.data[o+3]=0;continue;}
const [r,g,b]=lutRGB(v);
im.data[o]=r;im.data[o+1]=g;im.data[o+2]=b;
im.data[o+3]=Math.min(242,Math.pow(v/255,0.62)*300);
}
}
octx.putImageData(im,0,0);
ctx.imageSmoothingEnabled=true; ctx.imageSmoothingQuality='high';
const [vx0,vx1,vy0,vy1]=M.view;
const sx=vx0*nx, sw=(vx1-vx0)*nx;
const sy=(1-vy1)*ny, sh=(vy1-vy0)*ny; // image rows run north->south
ctx.drawImage(oc,sx,sy,sw,sh,0,0,W,H);
// outlines
ctx.save(); ctx.beginPath(); ctx.rect(0,0,W,H); ctx.clip();
ctx.strokeStyle='rgba(60,72,88,.42)'; ctx.lineWidth=Math.max(1,DPR*.7);
ctx.lineJoin='round';
for(const ln of M.lines){
ctx.beginPath();
for(let k=0;k<ln.length;k++){
const X=(ln[k][0]-vx0)/(vx1-vx0)*W, Y=(1-(ln[k][1]-vy0)/(vy1-vy0))*H;
k?ctx.lineTo(X,Y):ctx.moveTo(X,Y);}
ctx.stroke();
}
ctx.restore();
// outline the state with the most tornadoes, in Illini blue
const sp=M.state_polys[M.top_state[cur]];
if(sp){
ctx.save(); ctx.beginPath(); ctx.rect(0,0,W,H); ctx.clip();
ctx.lineJoin='round'; ctx.lineCap='round';
for(const pass of [0,1]){
const sw=Math.max(2.4,W*0.0038);
ctx.strokeStyle = pass ? '#13294B' : 'rgba(255,255,255,.85)';
ctx.lineWidth = pass ? sw : sw*2.1;
for(const pg of sp){
ctx.beginPath();
for(let k=0;k<pg.length;k++){
const X=(pg[k][0]-vx0)/(vx1-vx0)*W, Y=(1-(pg[k][1]-vy0)/(vy1-vy0))*H;
k?ctx.lineTo(X,Y):ctx.moveTo(X,Y);
}
ctx.closePath(); ctx.stroke();
}
}
ctx.restore();
}
// peak-density cell
{
const [pj,pi]=M.max_ij[cur];
const u0=pi/nx, u1=(pi+1)/nx, v0=pj/ny, v1=(pj+1)/ny;
const X0=(u0-vx0)/(vx1-vx0)*W, X1=(u1-vx0)/(vx1-vx0)*W;
const Y0=(1-(v1-vy0)/(vy1-vy0))*H, Y1=(1-(v0-vy0)/(vy1-vy0))*H;
ctx.save();
ctx.lineJoin='miter';
const lw=Math.max(2,W*0.0035), pad=lw; // scale with canvas, not device
ctx.strokeStyle='rgba(255,255,255,.95)'; // halo so it reads on dark reds
ctx.lineWidth=lw*1.9;
ctx.strokeRect(X0-pad,Y0-pad,(X1-X0)+pad*2,(Y1-Y0)+pad*2);
ctx.strokeStyle='#FF2D95';
ctx.lineWidth=lw;
ctx.strokeRect(X0-pad,Y0-pad,(X1-X0)+pad*2,(Y1-Y0)+pad*2);
ctx.restore();
}
// tornado points
const a=M.point_offsets[cur], b=M.point_offsets[cur+1];
ctx.fillStyle='rgba(10,12,16,.72)';
const rad=Math.max(0.85,W*0.0011);
for(let k=a;k<b;k++){
const u=PTS[k*2]/65535, v=PTS[k*2+1]/65535;
if(u<vx0||u>vx1||v<vy0||v>vy1) continue;
const X=(u-vx0)/(vx1-vx0)*W, Y=(1-(v-vy0)/(vy1-vy0))*H;
ctx.beginPath(); ctx.arc(X,Y,rad,0,6.2832); ctx.fill();
}
}
function drawTL(){
const W=tl.width,H=tl.height,n=M.years.length;
tctx.clearRect(0,0,W,H);
const pad=6*DPR, bw=(W-pad*2)/n, mx=Math.max(...M.annual_max);
for(let i=0;i<n;i++){
const h=(M.annual_max[i]/mx)*(H-26*DPR);
const x=pad+i*bw;
tctx.fillStyle = i===cur ? '#FF5F05' : (M.years[i]===2026?'#9AA7B8':'#D7DDE4');
tctx.fillRect(x,H-16*DPR-h,Math.max(1,bw-1*DPR),h);
}
tctx.fillStyle='#13294B';
tctx.fillRect(pad+cur*bw-1*DPR,0,Math.max(2,bw+1*DPR),H-16*DPR);
tctx.globalAlpha=.14; tctx.fillRect(pad+cur*bw-1*DPR,0,Math.max(2,bw+1*DPR),H-16*DPR);
tctx.globalAlpha=1;
tctx.fillStyle='#8A94A2'; tctx.font=(10.5*DPR)+'px "Source Sans 3",sans-serif';
for(const y of [1960,1970,1980,1990,2000,2010,2020]){
const i=M.years.indexOf(y), x=pad+i*bw+bw/2;
tctx.fillRect(x,H-14*DPR,1*DPR,4*DPR);
tctx.textAlign='center'; tctx.fillText(y,x,H-2*DPR);
}
}
function setYear(i){
cur=Math.max(0,Math.min(M.years.length-1,i));
const y=M.years[cur];
document.getElementById('cy').textContent=y;
document.getElementById('cp').style.display = (y===M.partial_year)?'block':'none';
document.getElementById('cmax').innerHTML=M.annual_max[cur].toFixed(2)+' <small>per 10,000 km²</small>';
const est=(M.years[cur]===M.partial_year)?' (est.)':'';
const tw=M.max_towns[cur]||[];
document.getElementById('ctowns').textContent = tw.length? ('near '+tw.join(' and ')) : '';
document.getElementById('cwhere').textContent=M.max_lat[cur].toFixed(1)+'°N, '+Math.abs(M.max_lon[cur]).toFixed(1)+'°W';
document.getElementById('crank').textContent='#'+M.rank[cur];
document.getElementById('ccount').innerHTML=M.count[cur].toLocaleString()+est+
' <small>#'+M.count_rank[cur]+' of 77</small>';
document.getElementById('cstate').textContent=STATE[M.top_state[cur]]||M.top_state[cur];
document.getElementById('cstaten').textContent=M.top_state_n[cur].toLocaleString()+' tornadoes'+est;
render(); drawTL();
}
function bind(){
const jump=e=>{
const r=tl.getBoundingClientRect(), pad=6;
const f=(e.clientX-r.left-pad)/(r.width-pad*2);
setYear(Math.round(f*(M.years.length-1)));
};
let drag=false;
tl.addEventListener('pointerdown',e=>{drag=true;tl.setPointerCapture(e.pointerId);jump(e);stop();});
tl.addEventListener('pointermove',e=>{if(drag)jump(e);});
tl.addEventListener('pointerup',()=>drag=false);
addEventListener('keydown',e=>{
if(e.key==='ArrowRight'||e.key==='ArrowUp'){setYear(cur+1);stop();e.preventDefault();}
if(e.key==='ArrowLeft'||e.key==='ArrowDown'){setYear(cur-1);stop();e.preventDefault();}
if(e.key===' '){toggle();e.preventDefault();}
});
let acc=0;
wrapWheel(document.querySelector('.mapwrap'));
function wrapWheel(el){
el.addEventListener('wheel',e=>{
const dir=e.deltaY>0?1:-1;
const atEnd=(dir>0&&cur>=M.years.length-1)||(dir<0&&cur<=0);
if(atEnd) return; // let the page scroll past the map
acc+=e.deltaY;
if(Math.abs(acc)>38){setYear(cur+(acc>0?1:-1));acc=0;stop();}
e.preventDefault();
},{passive:false});
}
document.getElementById('play').addEventListener('click',toggle);
let rt=null;
addEventListener('resize',()=>{clearTimeout(rt);rt=setTimeout(sizeAll,140);});
// horizontal swipe over the map changes year; vertical is left to the page
const wrap=document.querySelector('.mapwrap');
let sx=0,sy=0,sc=0,swiping=false;
wrap.addEventListener('pointerdown',e=>{
if(e.pointerType==='mouse')return;
sx=e.clientX; sy=e.clientY; sc=cur; swiping=false;
});
wrap.addEventListener('pointermove',e=>{
if(e.pointerType==='mouse'||!sx)return;
const dx=e.clientX-sx, dy=e.clientY-sy;
if(!swiping && Math.abs(dx)>12 && Math.abs(dx)>Math.abs(dy)*1.4){swiping=true;stop();}
if(swiping){setYear(sc+Math.round(dx/26)); e.preventDefault();}
},{passive:false});
wrap.addEventListener('pointerup',()=>{sx=0;swiping=false;});
wrap.addEventListener('pointercancel',()=>{sx=0;swiping=false;});
}
function toggle(){playing?stop():start();}
function start(){playing=true;document.getElementById('play').innerHTML='&#10073;&#10073;';
timer=setInterval(()=>{setYear(cur>=M.years.length-1?0:cur+1);},520);}
function stop(){playing=false;document.getElementById('play').innerHTML='&#9654;';
clearInterval(timer);}
</script>
</body>
</html>