<!-- Banach-Tarski schematic demo -->
<p class="hint">This schematic shows the Banach-Tarski idea: one ball split into 5 labeled pieces, then reassembled into two complete balls. The real pieces are non-measurable — they cannot be drawn — but the logic holds exactly.</p>
<div class="stage-label" id="stage-label">Original ball (5 non-measurable pieces)</div>
<div class="canvas-wrap">
<canvas id="cvs" width="340" height="180"></canvas>
</div>
<div class="status" id="status">One ball — ready to split.</div>
<div class="btns">
<button id="btn-split" type="button">Split into pieces</button>
<button id="btn-reassemble" type="button" disabled>Reassemble into two balls</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
<p class="footnote">Note: the real Banach-Tarski pieces cannot be visualized — they are built from the Axiom of Choice and are non-measurable. This diagram is a schematic only.</p>
/* Styles for the Banach-Tarski schematic demo */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: .5rem; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .6rem; line-height: 1.5; }
.stage-label { font-size: .8rem; font-weight: 700; letter-spacing: .06em;
text-transform: uppercase; color: #5a7088; margin-bottom: .3rem; }
.canvas-wrap { border: 1px solid #cdd9e3; border-radius: 10px; overflow: hidden;
background: #f4f7fa; display: inline-block; }
canvas { display: block; }
.status { font-size: .95rem; font-weight: 600; margin: .55rem 0 .4rem; min-height: 1.4em; color: #1d3557; }
.status.ok { color: #0a7d33; }
.status.info { color: #1d3557; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; transition: opacity .15s; }
button:disabled { opacity: .38; cursor: default; }
button.ghost { background: #fff; color: #1d3557; }
.footnote { font-size: .78rem; color: #666; margin: .6rem 0 0; line-height: 1.5; }
// Banach-Tarski paradox — schematic visualization
// The real pieces are non-measurable sets — they have no well-defined Lebesgue measure.
// We label them A-E and show a schematic split and reassembly into two balls.
const canvas = document.getElementById('cvs');
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
const CX = W / 2, CY = H / 2;
const R = 72; // radius of the original ball on canvas
// Five distinct colors for the five schematic pieces
const COLORS = ['#e63946','#2a9d8f','#e9c46a','#457b9d','#a8dadc'];
const PIECE_LABELS = ['A','B','C','D','E'];
// Demo phase state
let phase = 'original'; // original | split | two
// Draw the single original ball divided into 5 sectors
function drawOriginal() {
ctx.clearRect(0, 0, W, H);
// Draw five equal sectors, each representing one non-measurable piece
const sectors = 5;
for (let i = 0; i < sectors; i++) {
const start = (i / sectors) * 2 * Math.PI - Math.PI / 2;
const end = ((i + 1) / sectors) * 2 * Math.PI - Math.PI / 2;
ctx.beginPath();
ctx.moveTo(CX, CY);
ctx.arc(CX, CY, R, start, end);
ctx.closePath();
ctx.fillStyle = COLORS[i];
ctx.globalAlpha = 0.85;
ctx.fill();
ctx.globalAlpha = 1;
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
// Label each sector with its piece letter
const midAngle = (start + end) / 2;
const lx = CX + (R * 0.62) * Math.cos(midAngle);
const ly = CY + (R * 0.62) * Math.sin(midAngle);
ctx.fillStyle = '#fff';
ctx.font = 'bold 15px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(PIECE_LABELS[i], lx, ly);
}
// Draw the outer circle boundary
ctx.beginPath();
ctx.arc(CX, CY, R, 0, 2 * Math.PI);
ctx.strokeStyle = '#1d3557';
ctx.lineWidth = 2.2;
ctx.stroke();
}
// Draw the five pieces scattered apart
function drawSplit() {
ctx.clearRect(0, 0, W, H);
// Each piece shown as a small circle, scattered across the canvas
const positions = [
{ x: 80, y: 55 },
{ x: 200, y: 45 },
{ x: 310, y: 80 },
{ x: 110, y: 145 },
{ x: 255, y: 148 }
];
const r = 28; // radius of scattered piece circles
for (let i = 0; i < 5; i++) {
const px = positions[i].x, py = positions[i].y;
ctx.beginPath();
ctx.arc(px, py, r, 0, 2 * Math.PI);
ctx.fillStyle = COLORS[i];
ctx.globalAlpha = 0.85;
ctx.fill();
ctx.globalAlpha = 1;
ctx.strokeStyle = '#1d3557';
ctx.lineWidth = 1.6;
ctx.stroke();
ctx.fillStyle = '#fff';
ctx.font = 'bold 15px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(PIECE_LABELS[i], px, py);
}
}
// Draw two complete balls formed from the five pieces
function drawTwo() {
ctx.clearRect(0, 0, W, H);
const R2 = 62; // radius of each reassembled ball
// First ball uses pieces pieces A, C, E
const b1x = W / 4, b1y = H / 2;
const b1pieces = [0, 2, 4]; // indices for pieces A, C, E
const total1 = b1pieces.length;
for (let j = 0; j < total1; j++) {
const i = b1pieces[j];
const start = (j / total1) * 2 * Math.PI - Math.PI / 2;
const end = ((j + 1) / total1) * 2 * Math.PI - Math.PI / 2;
ctx.beginPath();
ctx.moveTo(b1x, b1y);
ctx.arc(b1x, b1y, R2, start, end);
ctx.closePath();
ctx.fillStyle = COLORS[i];
ctx.globalAlpha = 0.85;
ctx.fill();
ctx.globalAlpha = 1;
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
const midAngle = (start + end) / 2;
const lx = b1x + (R2 * 0.6) * Math.cos(midAngle);
const ly = b1y + (R2 * 0.6) * Math.sin(midAngle);
ctx.fillStyle = '#fff';
ctx.font = 'bold 14px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(PIECE_LABELS[i], lx, ly);
}
ctx.beginPath();
ctx.arc(b1x, b1y, R2, 0, 2 * Math.PI);
ctx.strokeStyle = '#1d3557';
ctx.lineWidth = 2.2;
ctx.stroke();
// Second ball uses pieces pieces B, D
const b2x = (3 * W) / 4, b2y = H / 2;
const b2pieces = [1, 3]; // indices for pieces B, D
const total2 = b2pieces.length;
for (let j = 0; j < total2; j++) {
const i = b2pieces[j];
const start = (j / total2) * 2 * Math.PI - Math.PI / 2;
const end = ((j + 1) / total2) * 2 * Math.PI - Math.PI / 2;
ctx.beginPath();
ctx.moveTo(b2x, b2y);
ctx.arc(b2x, b2y, R2, start, end);
ctx.closePath();
ctx.fillStyle = COLORS[i];
ctx.globalAlpha = 0.85;
ctx.fill();
ctx.globalAlpha = 1;
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
const midAngle = (start + end) / 2;
const lx = b2x + (R2 * 0.6) * Math.cos(midAngle);
const ly = b2y + (R2 * 0.6) * Math.sin(midAngle);
ctx.fillStyle = '#fff';
ctx.font = 'bold 14px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(PIECE_LABELS[i], lx, ly);
}
ctx.beginPath();
ctx.arc(b2x, b2y, R2, 0, 2 * Math.PI);
ctx.strokeStyle = '#1d3557';
ctx.lineWidth = 2.2;
ctx.stroke();
// Label the two balls below
ctx.fillStyle = '#1d3557';
ctx.font = '700 13px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.fillText('Ball 1 (A, C, E)', b1x, b1y + R2 + 6);
ctx.fillText('Ball 2 (B, D)', b2x, b2y + R2 + 6);
}
function setStatus(t, cls) {
const s = document.getElementById('status');
s.textContent = t;
s.className = 'status ' + cls;
}
function setStageLabel(t) {
document.getElementById('stage-label').textContent = t;
}
document.getElementById('btn-split').addEventListener('click', function () {
phase = 'split';
drawSplit();
setStageLabel('Pieces separated');
setStatus('5 pieces, scattered. Each has no well-defined volume.', 'info');
this.disabled = true;
document.getElementById('btn-reassemble').disabled = false;
});
document.getElementById('btn-reassemble').addEventListener('click', function () {
phase = 'two';
drawTwo();
setStageLabel('Two complete balls');
setStatus('Two balls — each the same size as the original. Volume is not conserved because the pieces are non-measurable.', 'ok');
this.disabled = true;
});
document.getElementById('btn-reset').addEventListener('click', function () {
phase = 'original';
drawOriginal();
setStageLabel('Original ball (5 non-measurable pieces)');
setStatus('One ball — ready to split.', 'info');
document.getElementById('btn-split').disabled = false;
document.getElementById('btn-reassemble').disabled = true;
});
// Initial render
drawOriginal();
setStatus('One ball — ready to split.', 'info');
<!-- Banach-Tarski schematic demo -->
<p class="hint">This schematic shows the Banach-Tarski idea: one ball split into 5 labeled pieces, then reassembled into two complete balls. The real pieces are non-measurable — they cannot be drawn — but the logic holds exactly.</p>
<div class="stage-label" id="stage-label">Original ball (5 non-measurable pieces)</div>
<div class="canvas-wrap">
<canvas id="cvs" width="340" height="180"></canvas>
</div>
<div class="status" id="status">One ball — ready to split.</div>
<div class="btns">
<button id="btn-split" type="button">Split into pieces</button>
<button id="btn-reassemble" type="button" disabled>Reassemble into two balls</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
<p class="footnote">Note: the real Banach-Tarski pieces cannot be visualized — they are built from the Axiom of Choice and are non-measurable. This diagram is a schematic only.</p>
/* Styles for the Banach-Tarski schematic demo */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: .5rem; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .6rem; line-height: 1.5; }
.stage-label { font-size: .8rem; font-weight: 700; letter-spacing: .06em;
text-transform: uppercase; color: #5a7088; margin-bottom: .3rem; }
.canvas-wrap { border: 1px solid #cdd9e3; border-radius: 10px; overflow: hidden;
background: #f4f7fa; display: inline-block; }
canvas { display: block; }
.status { font-size: .95rem; font-weight: 600; margin: .55rem 0 .4rem; min-height: 1.4em; color: #1d3557; }
.status.ok { color: #0a7d33; }
.status.info { color: #1d3557; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; transition: opacity .15s; }
button:disabled { opacity: .38; cursor: default; }
button.ghost { background: #fff; color: #1d3557; }
.footnote { font-size: .78rem; color: #666; margin: .6rem 0 0; line-height: 1.5; }
// Banach-Tarski paradox — schematic visualization
// The real pieces are non-measurable sets — they have no well-defined Lebesgue measure.
// We label them A-E and show a schematic split and reassembly into two balls.
const canvas = document.getElementById('cvs');
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
const CX = W / 2, CY = H / 2;
const R = 72; // radius of the original ball on canvas
// Five distinct colors for the five schematic pieces
const COLORS = ['#e63946','#2a9d8f','#e9c46a','#457b9d','#a8dadc'];
const PIECE_LABELS = ['A','B','C','D','E'];
// Demo phase state
let phase = 'original'; // original | split | two
// Draw the single original ball divided into 5 sectors
function drawOriginal() {
ctx.clearRect(0, 0, W, H);
// Draw five equal sectors, each representing one non-measurable piece
const sectors = 5;
for (let i = 0; i < sectors; i++) {
const start = (i / sectors) * 2 * Math.PI - Math.PI / 2;
const end = ((i + 1) / sectors) * 2 * Math.PI - Math.PI / 2;
ctx.beginPath();
ctx.moveTo(CX, CY);
ctx.arc(CX, CY, R, start, end);
ctx.closePath();
ctx.fillStyle = COLORS[i];
ctx.globalAlpha = 0.85;
ctx.fill();
ctx.globalAlpha = 1;
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
// Label each sector with its piece letter
const midAngle = (start + end) / 2;
const lx = CX + (R * 0.62) * Math.cos(midAngle);
const ly = CY + (R * 0.62) * Math.sin(midAngle);
ctx.fillStyle = '#fff';
ctx.font = 'bold 15px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(PIECE_LABELS[i], lx, ly);
}
// Draw the outer circle boundary
ctx.beginPath();
ctx.arc(CX, CY, R, 0, 2 * Math.PI);
ctx.strokeStyle = '#1d3557';
ctx.lineWidth = 2.2;
ctx.stroke();
}
// Draw the five pieces scattered apart
function drawSplit() {
ctx.clearRect(0, 0, W, H);
// Each piece shown as a small circle, scattered across the canvas
const positions = [
{ x: 80, y: 55 },
{ x: 200, y: 45 },
{ x: 310, y: 80 },
{ x: 110, y: 145 },
{ x: 255, y: 148 }
];
const r = 28; // radius of scattered piece circles
for (let i = 0; i < 5; i++) {
const px = positions[i].x, py = positions[i].y;
ctx.beginPath();
ctx.arc(px, py, r, 0, 2 * Math.PI);
ctx.fillStyle = COLORS[i];
ctx.globalAlpha = 0.85;
ctx.fill();
ctx.globalAlpha = 1;
ctx.strokeStyle = '#1d3557';
ctx.lineWidth = 1.6;
ctx.stroke();
ctx.fillStyle = '#fff';
ctx.font = 'bold 15px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(PIECE_LABELS[i], px, py);
}
}
// Draw two complete balls formed from the five pieces
function drawTwo() {
ctx.clearRect(0, 0, W, H);
const R2 = 62; // radius of each reassembled ball
// First ball uses pieces pieces A, C, E
const b1x = W / 4, b1y = H / 2;
const b1pieces = [0, 2, 4]; // indices for pieces A, C, E
const total1 = b1pieces.length;
for (let j = 0; j < total1; j++) {
const i = b1pieces[j];
const start = (j / total1) * 2 * Math.PI - Math.PI / 2;
const end = ((j + 1) / total1) * 2 * Math.PI - Math.PI / 2;
ctx.beginPath();
ctx.moveTo(b1x, b1y);
ctx.arc(b1x, b1y, R2, start, end);
ctx.closePath();
ctx.fillStyle = COLORS[i];
ctx.globalAlpha = 0.85;
ctx.fill();
ctx.globalAlpha = 1;
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
const midAngle = (start + end) / 2;
const lx = b1x + (R2 * 0.6) * Math.cos(midAngle);
const ly = b1y + (R2 * 0.6) * Math.sin(midAngle);
ctx.fillStyle = '#fff';
ctx.font = 'bold 14px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(PIECE_LABELS[i], lx, ly);
}
ctx.beginPath();
ctx.arc(b1x, b1y, R2, 0, 2 * Math.PI);
ctx.strokeStyle = '#1d3557';
ctx.lineWidth = 2.2;
ctx.stroke();
// Second ball uses pieces pieces B, D
const b2x = (3 * W) / 4, b2y = H / 2;
const b2pieces = [1, 3]; // indices for pieces B, D
const total2 = b2pieces.length;
for (let j = 0; j < total2; j++) {
const i = b2pieces[j];
const start = (j / total2) * 2 * Math.PI - Math.PI / 2;
const end = ((j + 1) / total2) * 2 * Math.PI - Math.PI / 2;
ctx.beginPath();
ctx.moveTo(b2x, b2y);
ctx.arc(b2x, b2y, R2, start, end);
ctx.closePath();
ctx.fillStyle = COLORS[i];
ctx.globalAlpha = 0.85;
ctx.fill();
ctx.globalAlpha = 1;
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
const midAngle = (start + end) / 2;
const lx = b2x + (R2 * 0.6) * Math.cos(midAngle);
const ly = b2y + (R2 * 0.6) * Math.sin(midAngle);
ctx.fillStyle = '#fff';
ctx.font = 'bold 14px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(PIECE_LABELS[i], lx, ly);
}
ctx.beginPath();
ctx.arc(b2x, b2y, R2, 0, 2 * Math.PI);
ctx.strokeStyle = '#1d3557';
ctx.lineWidth = 2.2;
ctx.stroke();
// Label the two balls below
ctx.fillStyle = '#1d3557';
ctx.font = '700 13px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.fillText('Ball 1 (A, C, E)', b1x, b1y + R2 + 6);
ctx.fillText('Ball 2 (B, D)', b2x, b2y + R2 + 6);
}
function setStatus(t, cls) {
const s = document.getElementById('status');
s.textContent = t;
s.className = 'status ' + cls;
}
function setStageLabel(t) {
document.getElementById('stage-label').textContent = t;
}
document.getElementById('btn-split').addEventListener('click', function () {
phase = 'split';
drawSplit();
setStageLabel('Pieces separated');
setStatus('5 pieces, scattered. Each has no well-defined volume.', 'info');
this.disabled = true;
document.getElementById('btn-reassemble').disabled = false;
});
document.getElementById('btn-reassemble').addEventListener('click', function () {
phase = 'two';
drawTwo();
setStageLabel('Two complete balls');
setStatus('Two balls — each the same size as the original. Volume is not conserved because the pieces are non-measurable.', 'ok');
this.disabled = true;
});
document.getElementById('btn-reset').addEventListener('click', function () {
phase = 'original';
drawOriginal();
setStageLabel('Original ball (5 non-measurable pieces)');
setStatus('One ball — ready to split.', 'info');
document.getElementById('btn-split').disabled = false;
document.getElementById('btn-reassemble').disabled = true;
});
// Initial render
drawOriginal();
setStatus('One ball — ready to split.', 'info');
Notice that the five colored regions together cover the original ball exactly once. After reassembly, each copy is built from a disjoint subset of those regions — and yet both copies are the same size as the original. The key insight is that non-measurable pieces have no well-defined volume, so "size is conserved" simply does not apply.
Comments
Loading comments...