Clock angle workbench — Calculate the smaller angle between continuously moving hour and minute hands and distinguish it from the larger separation measured…
HTML for this example
Select the code and use your copy shortcut. Changes in this preview are temporary.
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Clock angle workbench</title><style>
*{box-sizing:border-box}body{margin:0;background:#edf5fa;color:#14364a;font:16px/1.5 system-ui,sans-serif}main{max-width:1030px;margin:auto;padding:32px 28px}h1{font-size:clamp(28px,4vw,42px);letter-spacing:-.04em;margin:0 0 6px}p{margin:0 0 14px}.sub{color:#506b7b}.workspace{display:grid;grid-template-columns:1.05fr 1fr;gap:30px;background:white;padding:30px;border-radius:24px;margin-top:25px;box-shadow:0 12px 40px #23435d0b}.dial{width:100%;max-width:410px;display:block;margin:auto}.result{font-size:70px;font-weight:750;letter-spacing:-.07em;line-height:1.1;color:#155b79;margin:7px 0 16px}.small{font-size:14px;color:#506b7b}.fields{display:flex;gap:14px;margin-top:20px}label{font-size:14px;font-weight:700;flex:1}input{display:block;width:100%;font:24px system-ui;padding:9px 12px;border:1px solid #9bb3c1;border-radius:9px;margin-top:5px}button{min-height:46px;padding:10px 18px;border:0;border-radius:9px;background:#155b79;color:white;font:650 15px system-ui;cursor:pointer}.secondary{background:#e8f0f4;color:#23485d}.actions{display:flex;gap:10px;margin:15px 0}:focus-visible{outline:3px solid #d48027;outline-offset:3px}.error{color:#a32626;min-height:24px}.trace{margin-top:24px;display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:#cbdae4;border:1px solid #cbdae4;border-radius:12px;overflow:hidden}.trace div{background:#f9fcfe;padding:16px}.trace strong{display:block;font-size:23px}.trace span{font-size:13px;color:#536e7e}.legend{display:flex;justify-content:center;gap:18px;font-size:13px;margin-top:8px}.legend b{display:inline-block;width:23px;border-top:5px solid #16364c;vertical-align:middle;margin-right:5px}.legend .minute{border-top:3px dashed #ae5026}.below{max-width:75ch;margin:27px 0;color:#506b7b;font-size:14px}#diagram[hidden],#trace[hidden]{display:none}.stamp{font-size:12px;letter-spacing:.14em;text-transform:uppercase;font-weight:700;color:#547d90}@media(max-width:720px){main{padding:23px 18px}.workspace{grid-template-columns:1fr;padding:21px;gap:20px}.dial{max-width:320px}.result{font-size:60px}.trace{grid-template-columns:repeat(2,1fr)}}@media(max-width:400px){main{padding:20px 12px}.workspace{padding:16px}.result{font-size:55px}.trace div{padding:12px}}
</style></head><body><main><p class="stamp">Geometry workbench</p><h1>The angle between the hands</h1><p class="sub">Move the hour hand with the minutes, then compare both routes.</p><section class="workspace"><div><svg class="dial" viewBox="0 0 360 360" role="img" aria-labelledby="dial-title"><title id="dial-title">1:50: hour at 55 degrees, minute at 300 degrees, smaller angle 115 degrees</title><circle cx="180" cy="180" r="163" fill="#f6fafc" stroke="#b5cbd7" stroke-width="2"/><g id="ticks"></g><g id="numerals" font-size="20" font-family="system-ui" fill="#345466" text-anchor="middle"></g><g id="diagram"><path id="angle-arc" fill="none" stroke="#56a6c5" stroke-width="13"/><line id="hour-hand" x1="180" y1="180" x2="180" y2="80" stroke="#16364c" stroke-width="9" stroke-linecap="round"/><line id="minute-hand" x1="180" y1="180" x2="180" y2="42" stroke="#ae5026" stroke-width="5" stroke-dasharray="9 5" stroke-linecap="round"/><circle cx="180" cy="180" r="9" fill="#16364c"/></g></svg><div class="legend"><span><b></b>Hour · short solid</span><span><b class="minute"></b>Minute · long dashed</span></div></div><div><p class="stamp">Smaller separation</p><div id="result" class="result" role="status">115°</div><p id="other">Other separation: 245°</p><p id="case-note" class="small">At 1:50, the hour hand is 25° past 1.</p><div class="fields"><label for="hour">Hour · 1–12<input id="hour" value="1" inputmode="numeric" maxlength="4"></label><label for="minute">Minute · 0–59<input id="minute" value="50" inputmode="numeric" maxlength="4"></label></div><div class="actions"><button id="calculate" type="button">Calculate</button><button id="reset" type="button" class="secondary">Reset to 1:50</button></div><p id="error" class="error" role="alert"></p><p class="small">Angles run clockwise from 12. AM and PM have the same geometry.</p></div></section><section id="trace" class="trace" aria-label="Calculation trace"><div><span>Hour · 30(h mod 12) + 0.5m</span><strong id="hour-angle">55°</strong></div><div><span>Minute · 6m</span><strong id="minute-angle">300°</strong></div><div><span>Raw difference · |H − M|</span><strong id="raw-angle">245°</strong></div><div><span>Complement · 360 − raw</span><strong id="complement">115°</strong></div><div><span>Choose the smaller route</span><strong id="small-angle">115°</strong></div><div><span>Other route · 360 − smaller</span><strong id="large-angle">245°</strong></div></section><div class="below"><p>The ideal clock has a continuous hour hand: 30° per hour and 0.5° per minute. At 12:00 the hands coincide; the other 360° traversal is a full turn. At 6:00 both routes are 180°.</p><p>This model accepts whole minutes only. It does not read a device clock, include seconds, save inputs, or solve for every time a chosen angle occurs.</p></div></main><script>
'use strict';const $=id=>document.getElementById(id);const ns='http://www.w3.org/2000/svg';
function point(a,r){return[180+r*Math.sin(a*Math.PI/180),180-r*Math.cos(a*Math.PI/180)];}
for(let i=0;i<60;i++){let line=document.createElementNS(ns,'line');let a=point(i*6,i%5===0?146:153),b=point(i*6,158);['x1','y1','x2','y2'].forEach((key,j)=>line.setAttribute(key,[...a,...b][j]));line.setAttribute('stroke','#91aebe');line.setAttribute('stroke-width',i%5===0?'2':'1');$('ticks').appendChild(line);}
for(let i=1;i<=12;i++){let t=document.createElementNS(ns,'text'),p=point(i*30,128);t.setAttribute('x',p[0]);t.setAttribute('y',p[1]+7);t.textContent=i;$('numerals').appendChild(t);}
function clock(h,m){const H=30*(h%12)+m/2,M=6*m,d=Math.abs(H-M),small=Math.min(d,360-d);return{H,M,d,small,other:360-small,complement:360-d};}
function validate(h,m){const errors=[];for(const [label,value,min,max]of[['Hour',h,1,12],['Minute',m,0,59]])if(!/^[0-9]+$/.test(value.trim())||!Number.isSafeInteger(Number(value.trim()))||Number(value.trim())<min||Number(value.trim())>max)errors.push(label+' must be digit-only integer text from '+min+' to '+max+'.');return errors;}
function clear(){ $('result').textContent='Not calculated';$('result').style.fontSize='36px';$('other').textContent='';$('case-note').textContent='Calculate to show the current hand positions.';$('trace').hidden=true;$('diagram').hidden=true;$('dial-title').textContent='Clock outline; not calculated';for(const id of ['hour-angle','minute-angle','raw-angle','complement','small-angle','large-angle'])$(id).textContent='';const e=validate($('hour').value,$('minute').value);$('error').textContent=e.join(' ');}
function calculate(){clear();const h=$('hour').value.trim(),m=$('minute').value.trim(),e=validate(h,m);$('hour').setAttribute('aria-invalid',String(!/^[0-9]+$/.test(h)||+h<1||+h>12));$('minute').setAttribute('aria-invalid',String(!/^[0-9]+$/.test(m)||+m<0||+m>59));if(e.length)return;const r=clock(+h,+m);$('result').style.fontSize='';$('result').textContent=r.small+'°';$('other').textContent='Other separation: '+r.other+'°'+(r.small===0?' · full turn':'');$('case-note').textContent=r.small===0?'The hands coincide; there is no nonzero smaller arc.':r.small===180?'Equal half turns: both routes are 180°.':'At '+(+h)+':'+m.padStart(2,'0')+', the hour hand is '+(+m/2)+'° past '+(+h)+'.';$('trace').hidden=false;$('diagram').hidden=false;for(const [id,key]of[['hour-angle','H'],['minute-angle','M'],['raw-angle','d'],['complement','complement'],['small-angle','small'],['large-angle','other']])$(id).textContent=r[key]+'°';$('hour-hand').setAttribute('transform','rotate('+r.H+' 180 180)');$('minute-hand').setAttribute('transform','rotate('+r.M+' 180 180)');let start=r.H,end=r.M;if((r.M-r.H+360)%360>180){start=r.M;end=r.H;}let a=point(start,65),b=point(end,65);$('angle-arc').setAttribute('d',r.small===0?'':'M '+a.join(' ')+' A 65 65 0 0 1 '+b.join(' '));$('dial-title').textContent=(+h)+':'+m.padStart(2,'0')+': hour at '+r.H+' degrees, minute at '+r.M+' degrees, smaller angle '+r.small+' degrees';}
$('calculate').addEventListener('click',calculate);$('reset').addEventListener('click',()=>{$('hour').value='1';$('minute').value='50';calculate();});for(const id of ['hour','minute']){$(id).addEventListener('input',clear);$(id).addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.isComposing&&!e.repeat){e.preventDefault();calculate();}});}calculate();
</script></body></html>