Type any starting number and run it. Watch the sequence shoot up, plunge, and eventually hit 1 — and see its peak (the highest value reached) and its stopping time (how many steps it took).
<p class="hint">Type a starting number. <b>Even</b> → halve it; <b>odd</b> → triple it and add one. Watch the sequence climb and crash to 1. Checking one number is instant — proving every number reaches 1 is unsolved since 1937.</p>
<div class="controls">
<label>Start at <input id="seed" type="number" min="1" value="27"></label>
<button id="run" type="button">Run</button>
<button id="lucky" type="button" class="ghost">Random</button>
<button id="scan" type="button" class="ghost">Scan 1–10000</button>
</div>
<div class="stats" id="stats">Press Run to ride the hailstones.</div>
<canvas id="plot" width="460" height="180"></canvas>
<div class="seq" id="seq"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.controls { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin-bottom: .6rem; }
label { font-size: .9rem; font-weight: 600; }
input { width: 90px; font: 600 14px system-ui; padding: .35rem .5rem; border: 1px solid #adb1b8; border-radius: 8px; margin-left: .3rem; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557; background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.stats { font-size: 1rem; font-weight: 600; margin: .4rem 0; min-height: 1.4em; color: #1d3557; }
.stats.ok { color: #0a7d33; }
canvas { width: 100%; max-width: 460px; height: auto; background: #f3f6f9; border: 1px solid #cdd9e3; border-radius: 8px; display: block; }
.seq { font: 600 12px ui-monospace, monospace; color: #45586a; margin-top: .5rem; word-break: break-word; line-height: 1.5; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Collatz / 3n+1: even -> n/2, odd -> 3n+1. The conjecture (Lothar Collatz,
// ~1937; still OPEN) says every start eventually reaches 1. Checking ONE number
// is trivial and fast; proving it for ALL numbers is unsolved. We use BigInt so
// the peaks of long trajectories never overflow.
function collatz(n){
let seq = [n], peak = n, steps = 0;
while (n !== 1n){
n = (n % 2n === 0n) ? n / 2n : 3n * n + 1n;
if (n > peak) peak = n;
seq.push(n); steps++;
if (steps > 100000) break; // safety; never hit for tested numbers
}
return { seq, peak, steps, reached1: n === 1n };
}
function draw(seq){
const cv = document.getElementById('plot'), ctx = cv.getContext('2d');
const W = cv.width, H = cv.height, pad = 8;
ctx.clearRect(0,0,W,H);
const vals = seq.map(Number);
const max = Math.max(...vals, 1);
const n = vals.length;
ctx.strokeStyle = '#1d3557'; ctx.lineWidth = 2; ctx.beginPath();
vals.forEach((v,i) => {
const x = pad + (n === 1 ? 0 : i/(n-1)) * (W - 2*pad);
const y = H - pad - (v/max) * (H - 2*pad);
i ? ctx.lineTo(x,y) : ctx.moveTo(x,y);
});
ctx.stroke();
// mark the peak
const pi = vals.indexOf(max);
const px = pad + (n === 1 ? 0 : pi/(n-1)) * (W - 2*pad);
const py = H - pad - (max/max) * (H - 2*pad);
ctx.fillStyle = '#e63946'; ctx.beginPath(); ctx.arc(px,py,3.5,0,7); ctx.fill();
}
function run(){
const raw = document.getElementById('seed').value.trim();
let start;
try { start = BigInt(raw); } catch(e){ start = 0n; }
if (start < 1n){ document.getElementById('stats').textContent = 'Enter a whole number ≥ 1.'; return; }
const r = collatz(start);
const st = document.getElementById('stats');
st.className = 'stats ok';
st.textContent = fmt('✓ %start% reached 1 in %steps% steps · peak · peak %peak%', { start: start, steps: r.steps, peak: r.peak.toString() });
draw(r.seq);
const shown = r.seq.slice(0, 60).map(x => x.toString());
document.getElementById('seq').textContent =
shown.join(' → ') + (r.seq.length > 60 ? ' → … → 1' : '');
}
function scan(){
let worstSteps = 0, worstN = 1, allReached = true;
for (let i = 1; i <= 10000; i++){
const r = collatz(BigInt(i));
if (!r.reached1) allReached = false;
if (r.steps > worstSteps){ worstSteps = r.steps; worstN = i; }
}
const st = document.getElementById('stats');
st.className = 'stats ok';
st.textContent = allReached
? fmt('✓ All 10,000 numbers reached 1. Longest: %worstN% (%worstSteps% steps). Still not a proof!', { worstN: worstN, worstSteps: worstSteps })
: '✗ A number failed to reach 1 — that would be a discovery!';
document.getElementById('seed').value = worstN;
draw(collatz(BigInt(worstN)).seq);
document.getElementById('seq').textContent = fmt('Showing the longest trajectory found in 1–10000: start %worstN%.', { worstN: worstN });
}
document.getElementById('run').addEventListener('click', run);
document.getElementById('scan').addEventListener('click', scan);
document.getElementById('lucky').addEventListener('click', () => {
document.getElementById('seed').value = 1 + Math.floor(Math.random() * 100000);
run();
});
run();
<p class="hint">Type a starting number. <b>Even</b> → halve it; <b>odd</b> → triple it and add one. Watch the sequence climb and crash to 1. Checking one number is instant — proving every number reaches 1 is unsolved since 1937.</p>
<div class="controls">
<label>Start at <input id="seed" type="number" min="1" value="27"></label>
<button id="run" type="button">Run</button>
<button id="lucky" type="button" class="ghost">Random</button>
<button id="scan" type="button" class="ghost">Scan 1–10000</button>
</div>
<div class="stats" id="stats">Press Run to ride the hailstones.</div>
<canvas id="plot" width="460" height="180"></canvas>
<div class="seq" id="seq"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.controls { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin-bottom: .6rem; }
label { font-size: .9rem; font-weight: 600; }
input { width: 90px; font: 600 14px system-ui; padding: .35rem .5rem; border: 1px solid #adb1b8; border-radius: 8px; margin-left: .3rem; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557; background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.stats { font-size: 1rem; font-weight: 600; margin: .4rem 0; min-height: 1.4em; color: #1d3557; }
.stats.ok { color: #0a7d33; }
canvas { width: 100%; max-width: 460px; height: auto; background: #f3f6f9; border: 1px solid #cdd9e3; border-radius: 8px; display: block; }
.seq { font: 600 12px ui-monospace, monospace; color: #45586a; margin-top: .5rem; word-break: break-word; line-height: 1.5; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Collatz / 3n+1: even -> n/2, odd -> 3n+1. The conjecture (Lothar Collatz,
// ~1937; still OPEN) says every start eventually reaches 1. Checking ONE number
// is trivial and fast; proving it for ALL numbers is unsolved. We use BigInt so
// the peaks of long trajectories never overflow.
function collatz(n){
let seq = [n], peak = n, steps = 0;
while (n !== 1n){
n = (n % 2n === 0n) ? n / 2n : 3n * n + 1n;
if (n > peak) peak = n;
seq.push(n); steps++;
if (steps > 100000) break; // safety; never hit for tested numbers
}
return { seq, peak, steps, reached1: n === 1n };
}
function draw(seq){
const cv = document.getElementById('plot'), ctx = cv.getContext('2d');
const W = cv.width, H = cv.height, pad = 8;
ctx.clearRect(0,0,W,H);
const vals = seq.map(Number);
const max = Math.max(...vals, 1);
const n = vals.length;
ctx.strokeStyle = '#1d3557'; ctx.lineWidth = 2; ctx.beginPath();
vals.forEach((v,i) => {
const x = pad + (n === 1 ? 0 : i/(n-1)) * (W - 2*pad);
const y = H - pad - (v/max) * (H - 2*pad);
i ? ctx.lineTo(x,y) : ctx.moveTo(x,y);
});
ctx.stroke();
// mark the peak
const pi = vals.indexOf(max);
const px = pad + (n === 1 ? 0 : pi/(n-1)) * (W - 2*pad);
const py = H - pad - (max/max) * (H - 2*pad);
ctx.fillStyle = '#e63946'; ctx.beginPath(); ctx.arc(px,py,3.5,0,7); ctx.fill();
}
function run(){
const raw = document.getElementById('seed').value.trim();
let start;
try { start = BigInt(raw); } catch(e){ start = 0n; }
if (start < 1n){ document.getElementById('stats').textContent = 'Enter a whole number ≥ 1.'; return; }
const r = collatz(start);
const st = document.getElementById('stats');
st.className = 'stats ok';
st.textContent = fmt('✓ %start% reached 1 in %steps% steps · peak · peak %peak%', { start: start, steps: r.steps, peak: r.peak.toString() });
draw(r.seq);
const shown = r.seq.slice(0, 60).map(x => x.toString());
document.getElementById('seq').textContent =
shown.join(' → ') + (r.seq.length > 60 ? ' → … → 1' : '');
}
function scan(){
let worstSteps = 0, worstN = 1, allReached = true;
for (let i = 1; i <= 10000; i++){
const r = collatz(BigInt(i));
if (!r.reached1) allReached = false;
if (r.steps > worstSteps){ worstSteps = r.steps; worstN = i; }
}
const st = document.getElementById('stats');
st.className = 'stats ok';
st.textContent = allReached
? fmt('✓ All 10,000 numbers reached 1. Longest: %worstN% (%worstSteps% steps). Still not a proof!', { worstN: worstN, worstSteps: worstSteps })
: '✗ A number failed to reach 1 — that would be a discovery!';
document.getElementById('seed').value = worstN;
draw(collatz(BigInt(worstN)).seq);
document.getElementById('seq').textContent = fmt('Showing the longest trajectory found in 1–10000: start %worstN%.', { worstN: worstN });
}
document.getElementById('run').addEventListener('click', run);
document.getElementById('scan').addEventListener('click', scan);
document.getElementById('lucky').addEventListener('click', () => {
document.getElementById('seed').value = 1 + Math.floor(Math.random() * 100000);
run();
});
run();
Notice the asymmetry. Checking one number is effortless: the computer just applies the rules until it reaches 1, and it always does — quickly. But proving that every number behaves this way is something no chain of single checks can ever finish. Try the Scan 1–10000 button: thousands of starts, every one lands on 1. That is overwhelming evidence — and still not a proof.
Comments
Loading comments...