<!-- Two Envelopes Paradox demo: choose a prior, observe the gain from switching -->
<div class="controls">
<label for="prior-select">Prior distribution over amounts:</label>
<select id="prior-select">
<option value="uniform">Uniform on [$1, $50]</option>
<option value="geometric" selected>Geometric (powers of 2)</option>
<option value="exponential">Exponential (λ = 0.05)</option>
</select>
<label for="x-range" id="x-label">Your envelope contains: $ <span id="x-val">10</span></label>
<input type="range" id="x-range" min="1" max="100" value="10" step="1">
</div>
<div class="result-box" id="result-box">
<div class="result-row">
<span class="result-label">Your envelope ($x)</span>
<span class="result-value" id="val-x">$10</span>
</div>
<div class="result-row">
<span class="result-label">P(other = 2x | x)</span>
<span class="result-value" id="val-pa">—</span>
</div>
<div class="result-row">
<span class="result-label">P(other = x/2 | x)</span>
<span class="result-value" id="val-pb">—</span>
</div>
<div class="result-row">
<span class="result-label">E[other | x]</span>
<span class="result-value" id="val-ev">—</span>
</div>
<div class="result-row highlight">
<span class="result-label">Expected gain from switching</span>
<span class="result-value" id="val-gain">—</span>
</div>
</div>
<div class="verdict" id="verdict"></div>
<div class="explanation" id="explanation"></div>
<button id="btn-naive" type="button">Show the naive argument</button>
<div class="naive-box hidden" id="naive-box">
<strong>The naive argument (flawed):</strong>
<p>E[other] = ½ · (x/2) + ½ · (2x) = 5x/4. Since 5x/4 > x, always switch. Then apply the same logic to the new envelope — and switch again, forever.</p>
<p class="naive-warn">This assumes P(other = 2x) = P(other = x/2) = ½ regardless of x, which implies an improper flat prior over all positive reals — not a valid distribution.</p>
</div>
/* Styles for the Two Envelopes demo panel */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; }
.controls { display: flex; flex-direction: column; gap: .5rem; margin-bottom: .8rem; }
label { font-size: .85rem; color: #555; font-weight: 600; }
select, input[type=range] { width: 100%; }
select { padding: .3rem .4rem; border: 1px solid #ccc; border-radius: 6px; font-size: .9rem; }
.result-box { background: #f0f4f8; border-radius: 10px; padding: .8rem 1rem; margin-bottom: .6rem; }
.result-row { display: flex; justify-content: space-between; align-items: center;
padding: .2rem 0; border-bottom: 1px solid #dde3e9; font-size: .9rem; }
.result-row:last-child { border-bottom: none; }
.result-row.highlight { font-weight: 700; font-size: 1rem; }
.result-label { color: #555; }
.result-value { font-family: ui-monospace, monospace; }
.verdict { font-size: 1rem; font-weight: 700; min-height: 1.4em; margin-bottom: .3rem; }
.verdict.switch { color: #0a7d33; }
.verdict.keep { color: #c92f3c; }
.verdict.neutral { color: #555; }
.explanation { font-size: .82rem; color: #444; line-height: 1.5; margin-bottom: .6rem; }
button { font: 600 13px system-ui, sans-serif; padding: .4rem .8rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; margin-bottom: .5rem; }
.naive-box { background: #fff3cd; border: 1px solid #ffc107; border-radius: 8px;
padding: .7rem; font-size: .85rem; line-height: 1.5; }
.naive-box p { margin: .3rem 0 0; }
.naive-warn { color: #c92f3c; font-weight: 600; }
.hidden { display: none; }
// Two Envelopes: compute true expected gain from switching given a prior over the smaller amount
// Prior distributions over the smaller envelope amount m
function priorDensity(type, m) {
// Return the prior density (or mass) at m
if (m <= 0) return 0;
if (type === 'uniform') {
// Uniform prior: m equally likely in [1, 50]
return (m >= 1 && m <= 50) ? 1 / 50 : 0;
}
if (type === 'geometric') {
// Geometric prior: m is a power of 2 with geometric probability over exponent
const p = 0.5;
const k = Math.round(Math.log2(m));
if (k < 0) return 0;
return Math.abs(m - Math.pow(2, k)) < 0.01 ? Math.pow(1 - p, k) * p : 0;
}
if (type === 'exponential') {
// Exponential prior: m ~ Exp(lambda), continuous, decaying density
const lambda = 0.05;
return lambda * Math.exp(-lambda * m);
}
return 0;
}
function computeGain(type, x) {
// Posterior probabilities of Case A (your envelope is the smaller) and Case B (it is the larger)
const px = priorDensity(type, x);
const pxh = priorDensity(type, x / 2);
const total = px + pxh;
if (total < 1e-15) return { pA: 0.5, pB: 0.5, ev: x, gain: 0, degenerate: true };
const pA = px / total; // posterior prob your envelope is the smaller one
const pB = pxh / total; // posterior prob your envelope is the larger one
const ev = 2 * x * pA + (x / 2) * pB;
return { pA, pB, ev, gain: ev - x, degenerate: false };
}
const priorSelect = document.getElementById('prior-select');
const xRange = document.getElementById('x-range');
const xVal = document.getElementById('x-val');
const valX = document.getElementById('val-x');
const valPa = document.getElementById('val-pa');
const valPb = document.getElementById('val-pb');
const valEv = document.getElementById('val-ev');
const valGain = document.getElementById('val-gain');
const verdict = document.getElementById('verdict');
const explanation = document.getElementById('explanation');
const naiveBox = document.getElementById('naive-box');
const btnNaive = document.getElementById('btn-naive');
const EXPLANATIONS = {
switch_uniform: 'With a uniform prior on [$1,$50], x is within the support so both cases (m=x or m=x/2) are possible. The naive symmetry still holds here — but at x>$50 it breaks completely.',
keep_uniform: 'With a uniform prior on [$1,$50], x exceeds the maximum smaller amount ($50). Case A (m=x) is impossible — your envelope must be the larger one, so the other holds x/2. Keep it.',
neutral_uniform: 'At this value of x the prior assigns equal weight to both cases, making switching neither beneficial nor harmful.',
switch_geometric: 'With a geometric prior (powers of 2), the distribution is scale-free and symmetric — the gain is exactly zero. Switching is pointless. The naive 5x/4 argument is exposed as false.',
keep_geometric: 'With a geometric prior (powers of 2), the distribution is scale-free and symmetric — the gain is exactly zero.',
neutral_geometric: 'With a geometric prior (powers of 2), the distribution is symmetric across all scales — expected gain is always zero.',
switch_exponential: 'With a decaying exponential prior, small amounts are much more probable. Seeing a small x makes the higher value (2x) the likely other envelope.',
keep_exponential: 'With a decaying exponential prior, large x is very unlikely as the smaller amount. Your envelope is probably the larger one; the other holds x/2.',
neutral_exponential: 'At this value of x the exponential prior gives roughly equal posterior weight to both cases.'
};
const VERDICTS_SWITCH = 'Switch — expected gain is positive with this prior.';
const VERDICTS_KEEP = 'Keep — expected gain is negative; the other envelope is likely smaller.';
const VERDICTS_NEUTRAL = 'Indifferent — no gain expected from switching with this prior.';
function update() {
const type = priorSelect.value;
const x = Number(xRange.value);
xVal.textContent = x;
valX.textContent = '
Notice: with a uniform prior on a bounded range, switching is sometimes beneficial and sometimes harmful depending on x — there is no universal "always switch." With the geometric prior, the expected gain is exactly zero: the two envelopes are symmetric and swapping is irrelevant. The naive argument that gave +25% was implicitly using an improper flat prior over all positive reals, which does not define a valid probability distribution.
+ x;
const { pA, pB, ev, gain, degenerate } = computeGain(type, x);
valPa.textContent = (pA * 100).toFixed(1) + '%';
valPb.textContent = (pB * 100).toFixed(1) + '%';
valEv.textContent = '
Notice: with a uniform prior on a bounded range, switching is sometimes beneficial and sometimes harmful depending on x — there is no universal "always switch." With the geometric prior, the expected gain is exactly zero: the two envelopes are symmetric and swapping is irrelevant. The naive argument that gave +25% was implicitly using an improper flat prior over all positive reals, which does not define a valid probability distribution.
+ ev.toFixed(2);
valGain.textContent = (gain >= 0 ? '+' : '') + '
Notice: with a uniform prior on a bounded range, switching is sometimes beneficial and sometimes harmful depending on x — there is no universal "always switch." With the geometric prior, the expected gain is exactly zero: the two envelopes are symmetric and swapping is irrelevant. The naive argument that gave +25% was implicitly using an improper flat prior over all positive reals, which does not define a valid probability distribution.
+ gain.toFixed(2);
let state;
if (degenerate || Math.abs(gain) < 0.005) {
state = 'neutral';
verdict.textContent = VERDICTS_NEUTRAL;
verdict.className = 'verdict neutral';
} else if (gain > 0) {
state = 'switch';
verdict.textContent = VERDICTS_SWITCH;
verdict.className = 'verdict switch';
} else {
state = 'keep';
verdict.textContent = VERDICTS_KEEP;
verdict.className = 'verdict keep';
}
explanation.textContent = EXPLANATIONS[state + '_' + type] || '';
}
btnNaive.addEventListener('click', () => {
naiveBox.classList.toggle('hidden');
});
priorSelect.addEventListener('change', update);
xRange.addEventListener('input', update);
update();
<!-- Two Envelopes Paradox demo: choose a prior, observe the gain from switching -->
<div class="controls">
<label for="prior-select">Prior distribution over amounts:</label>
<select id="prior-select">
<option value="uniform">Uniform on [$1, $50]</option>
<option value="geometric" selected>Geometric (powers of 2)</option>
<option value="exponential">Exponential (λ = 0.05)</option>
</select>
<label for="x-range" id="x-label">Your envelope contains: $ <span id="x-val">10</span></label>
<input type="range" id="x-range" min="1" max="100" value="10" step="1">
</div>
<div class="result-box" id="result-box">
<div class="result-row">
<span class="result-label">Your envelope ($x)</span>
<span class="result-value" id="val-x">$10</span>
</div>
<div class="result-row">
<span class="result-label">P(other = 2x | x)</span>
<span class="result-value" id="val-pa">—</span>
</div>
<div class="result-row">
<span class="result-label">P(other = x/2 | x)</span>
<span class="result-value" id="val-pb">—</span>
</div>
<div class="result-row">
<span class="result-label">E[other | x]</span>
<span class="result-value" id="val-ev">—</span>
</div>
<div class="result-row highlight">
<span class="result-label">Expected gain from switching</span>
<span class="result-value" id="val-gain">—</span>
</div>
</div>
<div class="verdict" id="verdict"></div>
<div class="explanation" id="explanation"></div>
<button id="btn-naive" type="button">Show the naive argument</button>
<div class="naive-box hidden" id="naive-box">
<strong>The naive argument (flawed):</strong>
<p>E[other] = ½ · (x/2) + ½ · (2x) = 5x/4. Since 5x/4 > x, always switch. Then apply the same logic to the new envelope — and switch again, forever.</p>
<p class="naive-warn">This assumes P(other = 2x) = P(other = x/2) = ½ regardless of x, which implies an improper flat prior over all positive reals — not a valid distribution.</p>
</div>
/* Styles for the Two Envelopes demo panel */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; }
.controls { display: flex; flex-direction: column; gap: .5rem; margin-bottom: .8rem; }
label { font-size: .85rem; color: #555; font-weight: 600; }
select, input[type=range] { width: 100%; }
select { padding: .3rem .4rem; border: 1px solid #ccc; border-radius: 6px; font-size: .9rem; }
.result-box { background: #f0f4f8; border-radius: 10px; padding: .8rem 1rem; margin-bottom: .6rem; }
.result-row { display: flex; justify-content: space-between; align-items: center;
padding: .2rem 0; border-bottom: 1px solid #dde3e9; font-size: .9rem; }
.result-row:last-child { border-bottom: none; }
.result-row.highlight { font-weight: 700; font-size: 1rem; }
.result-label { color: #555; }
.result-value { font-family: ui-monospace, monospace; }
.verdict { font-size: 1rem; font-weight: 700; min-height: 1.4em; margin-bottom: .3rem; }
.verdict.switch { color: #0a7d33; }
.verdict.keep { color: #c92f3c; }
.verdict.neutral { color: #555; }
.explanation { font-size: .82rem; color: #444; line-height: 1.5; margin-bottom: .6rem; }
button { font: 600 13px system-ui, sans-serif; padding: .4rem .8rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; margin-bottom: .5rem; }
.naive-box { background: #fff3cd; border: 1px solid #ffc107; border-radius: 8px;
padding: .7rem; font-size: .85rem; line-height: 1.5; }
.naive-box p { margin: .3rem 0 0; }
.naive-warn { color: #c92f3c; font-weight: 600; }
.hidden { display: none; }
// Two Envelopes: compute true expected gain from switching given a prior over the smaller amount
// Prior distributions over the smaller envelope amount m
function priorDensity(type, m) {
// Return the prior density (or mass) at m
if (m <= 0) return 0;
if (type === 'uniform') {
// Uniform prior: m equally likely in [1, 50]
return (m >= 1 && m <= 50) ? 1 / 50 : 0;
}
if (type === 'geometric') {
// Geometric prior: m is a power of 2 with geometric probability over exponent
const p = 0.5;
const k = Math.round(Math.log2(m));
if (k < 0) return 0;
return Math.abs(m - Math.pow(2, k)) < 0.01 ? Math.pow(1 - p, k) * p : 0;
}
if (type === 'exponential') {
// Exponential prior: m ~ Exp(lambda), continuous, decaying density
const lambda = 0.05;
return lambda * Math.exp(-lambda * m);
}
return 0;
}
function computeGain(type, x) {
// Posterior probabilities of Case A (your envelope is the smaller) and Case B (it is the larger)
const px = priorDensity(type, x);
const pxh = priorDensity(type, x / 2);
const total = px + pxh;
if (total < 1e-15) return { pA: 0.5, pB: 0.5, ev: x, gain: 0, degenerate: true };
const pA = px / total; // posterior prob your envelope is the smaller one
const pB = pxh / total; // posterior prob your envelope is the larger one
const ev = 2 * x * pA + (x / 2) * pB;
return { pA, pB, ev, gain: ev - x, degenerate: false };
}
const priorSelect = document.getElementById('prior-select');
const xRange = document.getElementById('x-range');
const xVal = document.getElementById('x-val');
const valX = document.getElementById('val-x');
const valPa = document.getElementById('val-pa');
const valPb = document.getElementById('val-pb');
const valEv = document.getElementById('val-ev');
const valGain = document.getElementById('val-gain');
const verdict = document.getElementById('verdict');
const explanation = document.getElementById('explanation');
const naiveBox = document.getElementById('naive-box');
const btnNaive = document.getElementById('btn-naive');
const EXPLANATIONS = {
switch_uniform: 'With a uniform prior on [$1,$50], x is within the support so both cases (m=x or m=x/2) are possible. The naive symmetry still holds here — but at x>$50 it breaks completely.',
keep_uniform: 'With a uniform prior on [$1,$50], x exceeds the maximum smaller amount ($50). Case A (m=x) is impossible — your envelope must be the larger one, so the other holds x/2. Keep it.',
neutral_uniform: 'At this value of x the prior assigns equal weight to both cases, making switching neither beneficial nor harmful.',
switch_geometric: 'With a geometric prior (powers of 2), the distribution is scale-free and symmetric — the gain is exactly zero. Switching is pointless. The naive 5x/4 argument is exposed as false.',
keep_geometric: 'With a geometric prior (powers of 2), the distribution is scale-free and symmetric — the gain is exactly zero.',
neutral_geometric: 'With a geometric prior (powers of 2), the distribution is symmetric across all scales — expected gain is always zero.',
switch_exponential: 'With a decaying exponential prior, small amounts are much more probable. Seeing a small x makes the higher value (2x) the likely other envelope.',
keep_exponential: 'With a decaying exponential prior, large x is very unlikely as the smaller amount. Your envelope is probably the larger one; the other holds x/2.',
neutral_exponential: 'At this value of x the exponential prior gives roughly equal posterior weight to both cases.'
};
const VERDICTS_SWITCH = 'Switch — expected gain is positive with this prior.';
const VERDICTS_KEEP = 'Keep — expected gain is negative; the other envelope is likely smaller.';
const VERDICTS_NEUTRAL = 'Indifferent — no gain expected from switching with this prior.';
function update() {
const type = priorSelect.value;
const x = Number(xRange.value);
xVal.textContent = x;
valX.textContent = '
Notice: with a uniform prior on a bounded range, switching is sometimes beneficial and sometimes harmful depending on x — there is no universal "always switch." With the geometric prior, the expected gain is exactly zero: the two envelopes are symmetric and swapping is irrelevant. The naive argument that gave +25% was implicitly using an improper flat prior over all positive reals, which does not define a valid probability distribution.
+ x;
const { pA, pB, ev, gain, degenerate } = computeGain(type, x);
valPa.textContent = (pA * 100).toFixed(1) + '%';
valPb.textContent = (pB * 100).toFixed(1) + '%';
valEv.textContent = '
Notice: with a uniform prior on a bounded range, switching is sometimes beneficial and sometimes harmful depending on x — there is no universal "always switch." With the geometric prior, the expected gain is exactly zero: the two envelopes are symmetric and swapping is irrelevant. The naive argument that gave +25% was implicitly using an improper flat prior over all positive reals, which does not define a valid probability distribution.
+ ev.toFixed(2);
valGain.textContent = (gain >= 0 ? '+' : '') + '
Notice: with a uniform prior on a bounded range, switching is sometimes beneficial and sometimes harmful depending on x — there is no universal "always switch." With the geometric prior, the expected gain is exactly zero: the two envelopes are symmetric and swapping is irrelevant. The naive argument that gave +25% was implicitly using an improper flat prior over all positive reals, which does not define a valid probability distribution.
+ gain.toFixed(2);
let state;
if (degenerate || Math.abs(gain) < 0.005) {
state = 'neutral';
verdict.textContent = VERDICTS_NEUTRAL;
verdict.className = 'verdict neutral';
} else if (gain > 0) {
state = 'switch';
verdict.textContent = VERDICTS_SWITCH;
verdict.className = 'verdict switch';
} else {
state = 'keep';
verdict.textContent = VERDICTS_KEEP;
verdict.className = 'verdict keep';
}
explanation.textContent = EXPLANATIONS[state + '_' + type] || '';
}
btnNaive.addEventListener('click', () => {
naiveBox.classList.toggle('hidden');
});
priorSelect.addEventListener('change', update);
xRange.addEventListener('input', update);
update();