Inside
OBSERVED.
A transparent instrument room for understanding a simulated urban microclimate. The signature technique is accessible live canvas data instruments.
Separate the instruments
Temperature, humidity, and wind each get an independent canvas, vertical scale, unit, and current value. A single deterministic sum of sine waves generates each series; the data is explicitly simulated.
Draw at a bounded cadence
The feed adds one sample per second. Canvas pixel ratio is capped at two, and ResizeObserver handles layout changes. No animation loop is needed for these sparse updates.
Encode redundantly
Colors #D55E00, #0072B2, and #009E73 come from the established Okabe–Ito palette. Solid, dashed, and dotted strokes reinforce the mapping. Text names and units make color identification unnecessary.
Support inspection without a mouse
Pointer position maps to a sample index and updates an HTML live output. Focus selects the newest sample; left and right arrows inspect neighbors. Reduced motion starts the feed paused; the user can deliberately resume.
Reproduce the site
- Download the complete source below and keep its name
index.html. - Open it through a static server. From its directory, run
python3 -m http.server 8000, then visithttp://localhost:8000. There is no install or build step. - The stylesheet lives in the head. The rendering and interaction code lives in the final script. Adjust one parameter at a time and compare the result at desktop and 375 px widths.
- Google Fonts requires a network connection. All visible artwork is computed locally. Native system fallbacks preserve readable text if fonts fail.
- Enable your operating system’s reduced-motion setting and repeat the interaction. Use browser developer tools to check errors, overflow, and frame timing before publishing.
The complete rendering and interaction code
const rm=matchMedia('(prefers-reduced-motion: reduce)'),cfg=[{id:'temp',min:16,max:28,base:22,amp:3,color:'#d55e00',unit:'°C',dash:[]},{id:'humidity',min:30,max:80,base:55,amp:15,color:'#0072b2',unit:'%',dash:[6,3]},{id:'wind',min:0,max:12,base:5,amp:3,color:'#009e73',unit:'m/s',dash:[2,3]}];let tick=60,count=60,paused=rm.matches;const series=(s,n)=>s.base+s.amp*(Math.sin(n*.13+cfg.indexOf(s)*2)*.65+Math.sin(n*.037+1)*.35);function paint(s){const c=document.querySelector('#'+s.id),ctx=c.getContext('2d'),r=c.getBoundingClientRect(),d=Math.min(devicePixelRatio,2),w=r.width,h=r.height;c.width=w*d;c.height=h*d;ctx.scale(d,d);const left=33,top=20,right=w-8,bottom=h-30;ctx.font='12px Inconsolata,monospace';ctx.lineWidth=1;ctx.textAlign='right';for(let i=0;i<4;i++){const y=top+(bottom-top)*i/3,v=s.max-(s.max-s.min)*i/3;ctx.strokeStyle='#dfe7ed';ctx.beginPath();ctx.moveTo(left,y);ctx.lineTo(right,y);ctx.stroke();ctx.fillStyle='#465f75';ctx.fillText(v.toFixed(0),left-7,y+4)}ctx.strokeStyle=s.color;ctx.lineWidth=2;ctx.setLineDash(s.dash);ctx.beginPath();for(let i=0;i<count;i++){const v=series(s,tick-count+i),x=left+(right-left)*i/(count-1),y=bottom-(v-s.min)/(s.max-s.min)*(bottom-top);i?ctx.lineTo(x,y):ctx.moveTo(x,y)}ctx.stroke();ctx.setLineDash([]);ctx.fillStyle='#465f75';ctx.textAlign='left';ctx.fillText('-'+count+'s',left,h-8);ctx.textAlign='right';ctx.fillText('now',right,h-8);document.querySelector('#'+s.id+'-value').innerHTML=series(s,tick-1).toFixed(1)+' <small>'+s.unit+'</small>';c.setAttribute('aria-label',s.id+' chart. Latest '+series(s,tick-1).toFixed(1)+' '+s.unit+'. Use arrow keys to inspect.');if(s.selected!==undefined){const idx=s.selected,v=series(s,tick-count+idx),x=left+(right-left)*idx/(count-1),y=bottom-(v-s.min)/(s.max-s.min)*(bottom-top);ctx.fillStyle=s.color;ctx.beginPath();ctx.arc(x,y,5,0,Math.PI*2);ctx.fill();document.querySelector('#'+s.id+'-tip').textContent=(count-idx)+' seconds ago · '+v.toFixed(1)+' '+s.unit}}cfg.forEach(s=>{const c=document.querySelector('#'+s.id);new ResizeObserver(()=>paint(s)).observe(c);c.onpointermove=e=>{s.selected=Math.max(0,Math.min(count-1,Math.round((e.offsetX-33)/(c.clientWidth-41)*(count-1))));paint(s)};c.onfocus=()=>{s.selected=count-1;paint(s)};c.onkeydown=e=>{if(['ArrowLeft','ArrowRight'].includes(e.key)){e.preventDefault();s.selected=Math.max(0,Math.min(count-1,(s.selected??count-1)+(e.key==='ArrowLeft'?-1:1)));paint(s)}}});function state(){document.querySelector('#pause').setAttribute('aria-pressed',String(paused));document.querySelector('#pause').textContent=paused?'Resume feed':'Pause feed';document.querySelector('#status').textContent=paused?'SIMULATED FEED · PAUSED':'SIMULATED FEED · UPDATING EVERY SECOND'}document.querySelector('#pause').onclick=()=>{paused=!paused;state()};document.querySelector('#window').onchange=e=>{count=+e.target.value;cfg.forEach(s=>{s.selected=undefined;paint(s)})};setInterval(()=>{if(!paused&&!document.hidden){tick++;cfg.forEach(paint)}},1000);rm.addEventListener('change',()=>{paused=rm.matches;state()});state();cfg.forEach(paint)The complete visual system
:root{--body:'Inconsolata',monospace}body{background:#f4f7fa;color:#172c44;font-family:'Sora',sans-serif}header{background:#172c44;color:#fff;padding:22px 4vw;display:flex;justify-content:space-between;align-items:center}header b{letter-spacing:.15em;font-size:15px}header span{font:13px var(--body);color:#b7c9d9}.room{padding:45px 4vw 60px;max-width:1500px;margin:auto}.room-heading{display:flex;justify-content:space-between;gap:30px;align-items:flex-end;margin-bottom:40px}.room-heading h1{font-size:clamp(36px,4vw,60px);font-weight:500;letter-spacing:-.055em;line-height:1.15;margin:10px 0}.room-heading p{font:15px var(--body);margin:0}.controls{display:flex;gap:10px}button,select{font:14px var(--body);padding:12px 16px;border:1px solid #9aafc3;background:white;border-radius:4px}.charts{display:grid;grid-template-columns:repeat(3,1fr);gap:22px}.chart{background:white;border:1px solid #cbd6df;border-radius:6px;padding:24px;min-width:0}.chart-top{display:flex;justify-content:space-between;align-items:center;font:14px var(--body)}.chart-top i{width:10px;height:10px;border-radius:50%;background:var(--c)}.value{font-size:48px;letter-spacing:-.055em;margin:20px 0 0}.value small{font:18px var(--body);letter-spacing:0}.chart canvas{width:100%;height:220px;touch-action:pan-y;cursor:crosshair}.chart output{display:block;min-height:36px;font:13px var(--body);color:#425970}.caption{font:13px var(--body);display:flex;justify-content:space-between;border-top:1px solid #dee5ed;padding-top:14px}.notes{display:grid;grid-template-columns:1fr 2fr;gap:60px;margin-top:45px;padding-top:35px;border-top:1px solid #bacbd9}.notes h2{font-size:21px;font-weight:500;margin:0}.notes p{font-size:15px;max-width:75ch;margin:0}.status{display:flex;align-items:center;gap:10px;font:13px var(--body);margin-top:20px}.status:before{content:'';width:7px;height:7px;background:#0072b2;border-radius:50%}.colophon{border-top:1px solid #bacbd9}@media(max-width:950px){.charts{grid-template-columns:1fr}.chart canvas{height:200px}.room-heading{display:block}.controls{margin-top:25px}.value{font-size:42px}.notes{grid-template-columns:1fr;gap:20px}.chart{padding:20px}.room{padding-top:30px}.room-heading h1{font-size:39px}header span{font-size:12px}}Original fictional design study. No stock photography, model files, image textures, or sample audio.