Radical Approximation Lab
Drag the sliders and watch where square roots live on the number line. Trap √n between perfect squares,
test your guesses, and hunt the perfect square hiding inside — then try the challenges.
Where does √n live?
√50 ≈ 7.071
Controls
Challenges
Set the sliders (or type an answer), then press Check.
/* === PURE FUNCTIONS (tested by node) === */ function simplifyRadical(n){ if (n < 1 || Math.floor(n) !== n) return null; for (var i = Math.floor(Math.sqrt(n)); i >= 2; i--){ var sq = i * i; if (n % sq === 0) return { c:i, r:n / sq }; } return { c:1, r:n }; } function bracketInfo(n){ var v = Math.sqrt(n); var lo = Math.floor(v), hi = Math.ceil(v); return { lo:lo, hi:hi, val:v }; } function guessError(n, g){ return Math.abs(g - Math.sqrt(n)); } function huntInfo(n){ var s = simplifyRadical(n); return { c:s.c, r:s.r, sq:s.c * s.c, text: n + ' = ' + (s.c * s.c) + ' × ' + s.r }; } function numberLineSVG(n, guess){ var W = 640, H = 120, pad = 30; function X(x){ return pad + (x / 15) * (W - 2 * pad); } var v = Math.sqrt(n); var s = ''; s += ''; return s; } var CHALLENGES = [ { id:'c50', text:'Trap √50: get your guess within 0.1 of the true value', hint:'Hint: set n = 50, then nudge your guess until the error drops under 0.1.', input:false, check:function(S, typed){ var e = guessError(50, S.g); var ok = !(S.n !== 50 || e > 0.1001); return { pass:ok, message: ok ? 'Correct — your guess is within 0.1 of √50.' : 'Not yet — set n = 50 and get the error under 0.1 (now ' + e.toFixed(3) + ').' }; } }, { id:'c72', text:'Name the largest perfect square dividing 72', hint:'Hint: try the squares 4, 9, 16, 25, 36, 49… which is the biggest that divides 72?', input:true, inputLabel:'Perfect square:', check:function(S, typed){ var v = parseInt(String(typed).trim(), 10); var ok = (v === 36); return { pass:ok, message: ok ? 'Correct — 72 = 36 × 2, so √72 = 6√2.' : 'Not yet — ' + (String(typed).trim() || '(blank)') + ' is not the largest square divisor of 72.' }; } }, { id:'c20', text:'Get within 0.05 of √20', hint:'Hint: set n = 20. √20 is between 4 and 5 — zoom in with the guess slider.', input:false, check:function(S, typed){ var e = guessError(20, S.g); var ok = !(S.n !== 20 || e > 0.0501); return { pass:ok, message: ok ? 'Correct — within 0.05 of √20.' : 'Not yet — set n = 20 and get the error under 0.05 (now ' + e.toFixed(3) + ').' }; } }, { id:'c110', text:'Approximate √110 to within 0.5', hint:'Hint: set n = 110. Which two integers is √110 between?', input:false, check:function(S, typed){ var e = guessError(110, S.g); var ok = !(S.n !== 110 || e > 0.5001); return { pass:ok, message: ok ? 'Correct — within 0.5 of √110.' : 'Not yet — set n = 110 and get the error under 0.5 (now ' + e.toFixed(3) + ').' }; } } ]; function checkChallenge(id, S, typed){ for (var i = 0; i < CHALLENGES.length; i++){ if (CHALLENGES[i].id === id){ return CHALLENGES[i].check(S, typed); } } return { pass:false, message:'Unknown challenge.' }; } function guessVerdict(e){ if (e <= 0.0501) return { cls:'good', text:'Excellent — within 0.05!' }; if (e <= 0.1001) return { cls:'good', text:'Great — within 0.1.' }; if (e <= 0.5001) return { cls:'mid', text:'Close — within 0.5. Can you get closer?' }; return { cls:'far', text:'Keep nudging — use the perfect-square markers as anchors.' }; } /* === END PURE FUNCTIONS === */ function el(id){ return document.getElementById(id); } var S = { n:50, g:7.0 }; function draw(){ var b = bracketInfo(S.n); el('numline').innerHTML = numberLineSVG(S.n, S.g); el('eqDisplay').textContent = '√' + S.n + ' ≈ ' + b.val.toFixed(3) + ' (between ' + b.lo + ' and ' + b.hi + ')'; var e = guessError(S.n, S.g); var v = guessVerdict(e); el('legend').innerHTML = 'perfect squares' + ' · your guess' + ' · error ' + e.toFixed(3) + ' — ' + v.text; el('approxNote').textContent = '√' + S.n + ' lives between ' + b.lo + ' and ' + b.hi + ' because ' + b.lo + '² = ' + (b.lo * b.lo) + ' < ' + S.n + ' < ' + (b.hi * b.hi) + ' = ' + b.hi + '².'; var h = huntInfo(S.n); el('huntBox').innerHTML = '
'; } function syncLabels(){ el('nVal').textContent = S.n; el('gVal').textContent = Number(S.g).toFixed(1); } function buildChallenges(){ var html = ''; for (var i = 0; i < CHALLENGES.length; i++){ (function(ch, idx){ html += '
' + '
' + (ch.input ? '' : '') + ' ' + '
'; })(CHALLENGES[i], i); } el('challengeList').innerHTML = html; var btns = el('challengeList').querySelectorAll('button'); for (var k = 0; k < btns.length; k++){ btns[k].addEventListener('click', function(){ var id = this.getAttribute('data-ch'); var inp = document.getElementById('chi-' + id); var r = checkChallenge(id, S, inp ? inp.value : ''); var span = el('chr-' + id); span.textContent = r.message; span.style.color = r.pass ? '#047857' : '#b91c1c'; span.style.fontWeight = '600'; }); } } function initLab(){ el('nSlider').addEventListener('input', function(){ S.n = parseInt(this.value, 10); syncLabels(); draw(); }); el('gSlider').addEventListener('input', function(){ S.g = parseFloat(this.value); syncLabels(); draw(); }); el('resetBtn').addEventListener('click', function(){ S = { n:50, g:7.0 }; el('nSlider').value = 50; el('gSlider').value = 7; syncLabels(); draw(); }); syncLabels(); draw(); buildChallenges(); } if (!(typeof document === 'undefined' || !document.addEventListener)){ document.addEventListener('DOMContentLoaded', initLab); }