Radians ↔ degrees

HTML for this example

Select the code and use your copy shortcut. Changes in this preview are temporary.

<!doctype html><html lang="en-US"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Radians ↔ degrees</title><style>*{box-sizing:border-box} :root{color-scheme:light;--ink:#20303b;--muted:#536570;--line:#d3dce1;--accent:#245c76;--paper:#fff;--soft:#edf3f6}body{margin:0;background:#f3f5f5;color:var(--ink);font:16px/1.5 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}main{max-width:1080px;margin:auto;padding:30px 32px 64px}header{margin-bottom:20px}h1{font-size:clamp(1.5rem,3vw,2.15rem);line-height:1.18;letter-spacing:-.035em;margin:0 0 8px}h2{font-size:1.22rem;line-height:1.3;margin:0 0 16px}h3{font-size:1rem;margin:0 0 8px}p{margin:8px 0 16px}small,.muted{color:var(--muted)}.eyebrow{font-size:.76rem;letter-spacing:.13em;text-transform:uppercase;font-weight:750;color:var(--accent);margin-bottom:8px}.panel{background:var(--paper);border:1px solid var(--line);padding:24px;border-radius:16px}.grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:24px}.stack{display:grid;gap:18px}.fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}label{display:block;font-size:.9rem;font-weight:650}input,select,textarea,button{font:inherit}input,select,textarea{display:block;width:100%;min-width:0;margin-top:6px;border:1px solid #8b9ca6;border-radius:7px;padding:10px 12px;background:white;color:var(--ink)}input,select{min-height:46px}textarea{resize:vertical}button{border:1px solid var(--accent);border-radius:7px;padding:10px 16px;min-height:44px;background:var(--accent);color:white;cursor:pointer;font-weight:650}button.secondary{background:white;color:var(--accent)}button:hover{filter:brightness(.94)}:focus-visible{outline:3px solid #d06e1c;outline-offset:3px}.actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:18px}.answer{background:var(--ink);color:white;padding:26px;border-radius:14px}.answer .muted{color:#d9e5eb}.big{font-size:clamp(2.1rem,5vw,3.8rem);font-weight:750;line-height:1.16;letter-spacing:-.045em;margin:8px 0;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.subresult{font-size:1.2rem;font-weight:650} .formula{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere}.note{border-left:3px solid #b47029;padding:10px 14px;background:#fff5e5;margin:14px 0;color:#66451b}.error{color:#a22621;font-weight:650;min-height:24px;margin:8px 0}.below{margin-top:28px}table{border-collapse:collapse;width:100%;font-size:.94rem}th{text-align:left;color:var(--muted);font-weight:650;font-size:.84rem}th,td{padding:11px 10px;border-bottom:1px solid var(--line);vertical-align:top}th:first-child,td:first-child{padding-left:0}th:last-child,td:last-child{padding-right:0}details{border-top:1px solid var(--line);padding:16px 0}summary{cursor:pointer;font-weight:650}svg{display:block;width:100%;height:auto}.tag{display:inline-block;font-size:.78rem;font-weight:650;padding:3px 8px;border-radius:30px;background:var(--soft);color:var(--accent)}.rule{height:1px;background:var(--line);margin:20px 0}.checklabel{display:flex;gap:10px;align-items:flex-start;font-weight:500}.checklabel input{width:20px;min-height:20px;flex:none;margin:2px 0}pre{white-space:pre-wrap;word-break:break-word;font: .86rem/1.6 ui-monospace,Consolas,monospace;background:#edf1f3;padding:20px;border-radius:8px}a{color:var(--accent)}[hidden]{display:none!important}@media(max-width:720px){main{padding:22px 20px 40px}.grid{grid-template-columns:1fr;gap:18px}.panel{padding:20px}.answer{padding:22px}.big{font-size:2.65rem}}@media(max-width:400px){main{padding:18px 16px 36px}.fields{gap:12px}th,td{padding:9px 6px}.panel{padding:16px}.big{font-size:2.25rem}}@media print{body{background:white}main{max-width:none;padding:0}.actions,button{display:none}.panel{break-inside:avoid}details>*{display:block}}.angle-layout{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(240px,1fr);gap:24px;align-items:center}.dial{margin:0;text-align:center;background:#fff;border-radius:50% 50% 12px 12px;padding:8px 24px 20px}.dial svg{max-height:240px}.dial figcaption{font-size:.9rem}@media(max-width:600px){.angle-layout{grid-template-columns:1fr}.dial{max-width:320px;width:100%;margin:auto}.dial svg{max-height:190px}}</style></head><body><main><header><div class="eyebrow">Angle magnitude inspector</div><h1>Radians ↔ degrees</h1></header>
<p>Preserve the signed angle; wrap only the separate direction sketch.</p>
<div class="angle-layout"><section class="answer" aria-live="polite"><div id="source">1 radian</div><div class="big" id="result">≈ 57.295779513°</div><p class="formula" id="derive">1 × 180/π ≈ 57.295779513°</p><p id="exact"></p></section><figure class="dial"><svg viewBox="0 0 300 250" role="img" aria-labelledby="dialTitle dialDesc"><title id="dialTitle">Wrapped endpoint direction</title><desc id="dialDesc">Counterclockwise from the rightward starting ray.</desc><circle cx="150" cy="120" r="85" fill="none" stroke="#cbd8da" stroke-width="2"/><path d="M45 120H255M150 15V225" stroke="#dce3e5"/><text x="251" y="144" font-size="14">0°</text><text x="158" y="24" font-size="14">90°</text><g id="sketch"><path id="arc" d="" fill="none" stroke="#b98129" stroke-width="5"/><line id="ray" x1="150" y1="120" x2="196" y2="48" stroke="#245c76" stroke-width="4"/><circle id="tip" cx="196" cy="48" r="6" fill="#245c76"/></g></svg><figcaption><b>Wrapped direction</b><div id="orientation">57.295779513°</div><small>Positive angles: counterclockwise from right.</small></figcaption></figure></div>
<section class="panel below"><div class="fields"><label for="angle">Supplied numeric angle<input id="angle" inputmode="decimal" value="1" ></label><label for="direction">Convert<select id="direction"><option value="rad">Radians to degrees</option><option value="deg">Degrees to radians</option></select></label></div><div class="actions"><button type="button" id="calculate">Convert</button><button type="button" id="preset90" class="secondary">90° preset</button><button type="button" id="preset450" class="secondary">450° preset</button><button type="button" id="reset" class="secondary">Reset 1 rad</button></div><p id="error" class="error" role="status"></p></section>
<section class="panel below"><h2>Magnitude record</h2><p id="turns" class="subresult"></p><p id="record" class="formula"></p></section><details class="below"><summary>Precision and supported input</summary><p>Enter a signed US decimal number; no symbolic expressions or scientific notation. For π/2 use the 90° preset, or enter a decimal approximation in radians. Zero is valid; blank is not. Numeric outputs round to nine places, using nearest rounding with ties away from zero. Calculations use the source angle and JavaScript’s numeric π; displayed π references come from degree fractions, not expression parsing.</p><p>The sketch shows a wrapped endpoint, never a replacement for the full angle. Extremely large values that cannot retain a useful direction at floating-point precision are rejected (magnitude limit: 1,000,000,000). This is not a sensor, temperature, DMS, map, or solid-angle converter. Entries are not saved.</p></details></main><script>
const $=id=>document.getElementById(id);const put=(id,text)=>{$(id).textContent=text};const fmt=(x,n=6)=>{if(!Number.isFinite(x))throw Error('Result exceeds the numeric range.');let v=(Math.round(Math.abs(x)*10**n)/10**n)*(x<0?-1:1);return Object.is(v,-0)?'0':v.toFixed(n).replace(/\.?0+$/,'')||'0'};const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
function decimal(s,{max=1000000,signed=false,places=6}={}){s=String(s).trim();if(!s)throw Error('Enter a value; a blank field is not zero.');const r=new RegExp('^'+(signed?'[+-]?':'')+'(?:[0-9]+(?:\\.[0-9]{1,'+places+'})?|\\.[0-9]{1,'+places+'})$');if(!r.test(s))throw Error('Use a '+(signed?'signed':'nonnegative')+' US decimal with up to '+places+' decimal places; no commas or exponents.');const v=Number(s);if(!Number.isFinite(v)||Math.abs(v)>max)throw Error('Enter a value within '+(signed?'-':'0 to ')+max+'.');return v;}
function scaled(s){decimal(s);const [a,b='']=String(s).trim().split('.');return BigInt(a||'0')*1000000n+BigInt(b.padEnd(6,'0'));}
function rational(n,d,places=6){if(d===0n)throw Error('Zero denominator');let neg=(n<0n)!=(d<0n);n=n<0n?-n:n;d=d<0n?-d:d;const scale=10n**BigInt(places),v=n*scale,q=v/d,r=v%d;let rounded=q+(r*2n>=d?1n:0n);let s=(rounded/scale).toString();const f=(rounded%scale).toString().padStart(places,'0').replace(/0+$/,'');if(f)s+='.'+f;return {text:(neg&&rounded!==0n?'-':'')+s,approx:r!==0n};}
function bind(ids,fn){ids.forEach(id=>{ $(id).addEventListener('input',fn);$(id).addEventListener('change',fn); });}

function angleConvert(s,d){if(/pi|π/i.test(s))throw Error('Enter a decimal approximation or use the 90° preset; symbolic expressions are not parsed.');const v=decimal(s,{max:1000000000,signed:true,places:12});if(!['rad','deg'].includes(d))throw Error('Choose a conversion direction.');const degrees=d==='rad'?v*180/Math.PI:v,radians=d==='deg'?v*Math.PI/180:v,out=d==='rad'?degrees:radians;if(!Number.isFinite(out))throw Error('Conversion overflow.');return {v,degrees,radians,out,wrapped:((degrees%360)+360)%360,turns:Math.trunc(degrees/360),remainder:degrees%360};}
function gcd(a,b){while(b){const x=a%b;a=b;b=x}return a;}
function piRef(v){if(!Number.isInteger(v))return '';if(v===0)return 'Exact reference: 0 rad';const g=gcd(Math.abs(v),180),n=v/g,d=180/g;return 'Exact reference: '+(n===1?'':n===-1?'-':n)+'π'+(d===1?'':'/'+d)+' rad';}
function update(){try{const r=angleConvert($('angle').value,$('direction').value),d=$('direction').value,unit=d==='rad'?'°':' rad',inputUnit=d==='rad'?' rad':'°';put('source',$('angle').value+inputUnit);put('result',(r.out===0?'':'≈ ')+fmt(r.out,9)+unit);put('derive',$('angle').value+' × '+(d==='rad'?'180/π':'π/180')+' '+(r.out===0?'=':'≈')+' '+fmt(r.out,9)+unit);put('exact',d==='deg'?piRef(r.v):r.v===1?'Exact reference: 180/π degrees':'');put('orientation',fmt(r.wrapped,9)+'° endpoint orientation');put('turns',fmt(r.degrees,9)+'° original magnitude = '+r.turns+' full signed turn(s) + '+fmt(r.remainder,9)+'°');put('record',$('angle').value+inputUnit+' → '+fmt(r.out,9)+unit+'; endpoint only: '+fmt(r.wrapped,9)+'°.');const t=r.wrapped*Math.PI/180,x=150+85*Math.cos(t),y=120-85*Math.sin(t);$('ray').setAttribute('x2',x);$('ray').setAttribute('y2',y);$('tip').setAttribute('cx',x);$('tip').setAttribute('cy',y);$('arc').setAttribute('d',r.wrapped===0?'':'M235 120 A85 85 0 '+(r.wrapped>180?1:0)+' 0 '+x+' '+y);$('sketch').setAttribute('visibility','visible');put('dialDesc','Wrapped endpoint '+fmt(r.wrapped,9)+' degrees; original magnitude '+fmt(r.degrees,9)+' degrees.');put('error','');}catch(e){put('result','—');['source','derive','exact','orientation','turns','record'].forEach(id=>put(id,''));$('sketch').setAttribute('visibility','hidden');put('dialDesc','No valid current angle; the endpoint sketch is hidden.');put('error',e.message);}}
bind(['angle','direction'],update);$('calculate').addEventListener('click',update);for(const [id,v] of [['preset90','90'],['preset450','450']])$(''+id).addEventListener('click',()=>{$('angle').value=v;$('direction').value='deg';update()});$('reset').addEventListener('click',()=>{$('angle').value='1';$('direction').value='rad';update()});globalThis.Tool={angleConvert,update};update();
</script></body></html>