Animated step-through of the computability argument against biological naturalism: simulate a brain, ask it if it's conscious, and follow the logic.
---
format: typebulb/v1
name: Simulated Brain Argument
---
**code.tsx**
```tsx
import React, { useEffect, useRef, useState } from "react"
import { createRoot } from "react-dom/client"
type Pt = [number, number]
const NODES: Pt[] = [
[-108, -28], [-76, -66], [-30, -88], [22, -92], [72, -68], [108, -34],
[122, 12], [86, 56], [38, 80], [-12, 84], [-66, 64], [-112, 22],
[-44, -24], [8, -40], [58, -8], [-2, 28], [62, 38], [-64, 2],
]
const EDGES: Array<[number, number]> = []
for (let i = 0; i < NODES.length; i++)
for (let j = i + 1; j < NODES.length; j++) {
const dx = NODES[i][0] - NODES[j][0]
const dy = NODES[i][1] - NODES[j][1]
if (Math.hypot(dx, dy) < 80) EDGES.push([i, j])
}
const clamp01 = (x: number) => Math.min(1, Math.max(0, x))
const smooth = (x: number) => { const c = clamp01(x); return c * c * (3 - 2 * c) }
const ph = (i: number) => (i * 0.618034) % 1
const pulse = (t: number, p: number) =>
Math.max(0, Math.sin(2 * Math.PI * (t * 0.35 + p))) ** 6
const REPORT = "I am conscious — I feel this."
const BITS = "01101001110100110110100111010011"
type Layout = {
vw: number; vh: number
bio: Pt; sim: Pt
anno: Pt
drift: Pt
bioLabelY: number; simLabelY: number
bioBubbleTop: number; simBubbleTop: number
hypoY: number; tagY: number
stamp: Pt
vertical: boolean
}
const LANDSCAPE: Layout = {
vw: 900, vh: 500,
bio: [235, 205], sim: [665, 205],
anno: [235, 66],
drift: [-240, -170],
bioLabelY: 342, simLabelY: 342,
bioBubbleTop: 370, simBubbleTop: 370,
hypoY: 70, tagY: 448,
stamp: [450, 235],
vertical: false,
}
const PORTRAIT: Layout = {
vw: 320, vh: 540,
bio: [160, 130], sim: [160, 392],
anno: [160, 16],
drift: [-120, -130],
bioLabelY: 246, simLabelY: 508,
bioBubbleTop: 180, simBubbleTop: 442,
hypoY: 268, tagY: 524,
stamp: [160, 268],
vertical: true,
}
const STEPS: Array<{ title: string; cap: string }> = [
{
title: "A brain, and a glow",
cap: "Here is a biological brain, neurons firing. The gold glow stands for consciousness — there is something it is like to be this brain. Could anything non-biological ever have the glow? Step through →",
},
{
title: "1 · Brains run on computable physics",
cap: "Every firing neuron runs on ordinary physics, and that physics is computable. The simulation needn't be perfect — just more faithful than the brain's own noise. Neurons compile to bits.",
},
{
title: "2 · So a brain can be simulated",
cap: "Run the physics of every neuron in software. The simulation has the exact same causal structure — each unit fires precisely when its biological twin does.",
},
{
title: "3 · It says it's conscious",
cap: "Ask it. The simulated brain reports consciousness — and that report is produced by the very same causal chain that produces yours.",
},
{
title: "4 · Suppose it isn't",
cap: "Hypothesis: no inner light. Notice that nothing about the words changes — the report is fully produced by the simulated neurons. Its consciousness-talk would have nothing to do with consciousness.",
},
{
title: "5 · Then your glow floats free",
cap: "Not gone \u2014 worse: idle. If reports aren't about consciousness, your glow plays no part in producing \u201cI feel this\u201d \u2014 the neurons would say it anyway. Consciousness floats off like a ghost: present, perhaps, but unable to touch the words.",
},
{
title: "6 · That's absurd",
cap: "A ghost that has nothing to do with you talking about it? You say “I feel this” because you feel it — consciousness drives its own report. Reject the hypothesis: the glow snaps back into the causal chain — and that same chain lights the simulation at the same instant.",
},
{
title: "7 · Biological naturalism is ruled out",
cap: "Consciousness cannot require the biological substrate — a silicon system with the same causal structure has it too. It's the organization, not the meat.",
},
]
function Network(props: { cx: number; cy: number; t: number; color: string; alpha: number; square?: boolean }) {
const { cx, cy, t, color, alpha, square } = props
if (alpha <= 0.01) return null
return (
<g transform={`translate(${cx} ${cy})`} opacity={alpha}>
{EDGES.map(([a, b], k) => {
const [x1, y1] = NODES[a]
const [x2, y2] = NODES[b]
const f = (t * 0.22 + ph(k + 5)) % 1
return (
<g key={k}>
<line x1={x1} y1={y1} x2={x2} y2={y2} stroke={color} strokeOpacity={0.16} strokeWidth={1} />
<circle cx={x1 + (x2 - x1) * f} cy={y1 + (y2 - y1) * f} r={1.6} fill={color} opacity={0.7} />
</g>
)
})}
{NODES.map(([x, y], i) => {
const b = pulse(t, ph(i))
const r = 3.5 + 3 * b
return square ? (
<rect key={i} x={x - r} y={y - r} width={2 * r} height={2 * r} rx={1.5} fill={color} opacity={0.45 + 0.55 * b} />
) : (
<circle key={i} cx={x} cy={y} r={r} fill={color} opacity={0.45 + 0.55 * b} />
)
})}
</g>
)
}
function Glow({ cx, cy, t, amount }: { cx: number; cy: number; t: number; amount: number }) {
if (amount <= 0.01) return null
const breathe = 0.88 + 0.12 * Math.sin(t * 1.4)
return (
<ellipse cx={cx} cy={cy} rx={175 * breathe} ry={135 * breathe} fill="url(#glowG)" opacity={0.75 * amount} />
)
}
function Bubble({ cx, top, text, typing, tail }: { cx: number; top: number; text: string; typing: boolean; tail: boolean }) {
const w = 272, h = 46
return (
<g>
{tail && <path d={`M ${cx - 11} ${top + 1} L ${cx} ${top - 13} L ${cx + 11} ${top + 1} Z`} fill="#161e33" stroke="#3a4a72" />}
<rect x={cx - w / 2} y={top} width={w} height={h} rx={10} fill="#161e33" stroke="#3a4a72" />
{tail && <rect x={cx - 12} y={top - 1} width={24} height={3} fill="#161e33" />}
<text x={cx} y={top + h / 2 + 5} textAnchor="middle" fontSize={14} fontFamily="ui-monospace, SFMono-Regular, Menlo, monospace" fill="#e8ecf6">
{"\u201c" + text + (typing ? "\u258c" : "\u201d")}
</text>
</g>
)
}
function App() {
const [step, setStep] = useState(0)
const [t, setT] = useState(0)
const tRef = useRef(0)
const stepT0 = useRef(0)
const wrapRef = useRef<HTMLDivElement>(null)
const [narrow, setNarrow] = useState(false)
useEffect(() => {
const el = wrapRef.current
if (!el) return
const ro = new ResizeObserver(() => setNarrow(el.clientWidth < 600))
ro.observe(el)
return () => ro.disconnect()
}, [])
useEffect(() => {
let raf = 0
const t0 = performance.now()
const loop = (now: number) => {
tRef.current = (now - t0) / 1000
setT(tRef.current)
raf = requestAnimationFrame(loop)
}
raf = requestAnimationFrame(loop)
return () => cancelAnimationFrame(raf)
}, [])
const goTo = (s: number) => {
stepT0.current = tRef.current
setStep(Math.max(0, Math.min(STEPS.length - 1, s)))
}
const ts = t - stepT0.current
const L = narrow ? PORTRAIT : LANDSCAPE
// -- per-step visual amounts --
const simNet = step < 2 ? 0 : step === 2 ? smooth(ts / 0.8) : 1
const placeholder = step < 2 ? 1 : step === 2 ? 1 - smooth(ts / 0.8) : 0
const grid = step === 0 ? 0 : step === 1 ? smooth(ts / 0.8) : 1
const physics = step === 1 ? smooth(ts / 0.6) : step >= 2 ? 0.13 : 0
const mapLines = step === 2 ? smooth(ts / 0.5) : 0
const drift = step === 5 ? smooth((ts - 0.9) / 1.6) : step === 6 ? 1 - smooth((ts - 1.1) / 1.2) : 0
const bioGlow = 1 - 0.45 * drift
const rightGlow = step === 6 ? smooth((ts - 1.1) / 1.2) : step === 7 ? 1 : 0
const bubbles = step >= 3
const typing = step === 3
const chars = typing ? Math.min(REPORT.length, Math.floor(ts * 16)) : REPORT.length
const stamp = step === 7 ? smooth(ts * 2.2) * (1 - smooth((ts - 1.6) / 2)) : 0
const stampScale = step === 7 ? 1.25 - 0.25 * smooth(ts * 2.8) : 1
const strike = step === 6 ? smooth(ts / 0.5) : 0
const bitOff = Math.floor(t * 6)
const bitRow = (k: number) => (BITS + BITS).slice((bitOff + k * 7) % BITS.length, ((bitOff + k * 7) % BITS.length) + 17)
return (
<div className="wrap" ref={wrapRef}>
<h1>Is a simulated brain conscious?</h1>
<div className="sub">The computability argument against biological naturalism.</div>
<svg className="scene" viewBox={`0 0 ${L.vw} ${L.vh}`} role="img" aria-label="Two brains, biological and simulated, stepping through the argument">
<defs>
<radialGradient id="glowG">
<stop offset="0%" stopColor="#ffd98c" stopOpacity="0.85" />
<stop offset="55%" stopColor="#ffc35e" stopOpacity="0.3" />
<stop offset="100%" stopColor="#ffc35e" stopOpacity="0" />
</radialGradient>
<clipPath id="simClip">
<ellipse cx={L.sim[0]} cy={L.sim[1]} rx={142} ry={102} />
</clipPath>
<marker id="arr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M0 0 L10 5 L0 10 Z" fill="#7ea0c8" />
</marker>
</defs>
{/* glows */}
<Glow
cx={L.bio[0] + L.drift[0] * drift + Math.sin(t * 0.8) * 8 * drift}
cy={L.bio[1] + L.drift[1] * drift + Math.sin(t * 1.3) * 6 * drift}
t={t} amount={bioGlow}
/>
<Glow cx={L.sim[0]} cy={L.sim[1]} t={t} amount={rightGlow} />
{/* consciousness annotation (early steps) */}
{step <= 1 && (
<text x={L.anno[0]} y={L.anno[1]} textAnchor="middle" fontSize={13} fill="#ffcf6e" fontStyle="italic">
the glow = consciousness
</text>
)}
{/* biological brain */}
<Network cx={L.bio[0]} cy={L.bio[1]} t={t} color="#ff8e72" alpha={1} />
<text x={L.bio[0]} y={L.bioLabelY} textAnchor="middle" fontSize={12} fill="#8fa0c0">biological brain</text>
{/* placeholder, grid substrate, simulated brain */}
{placeholder > 0.01 && (
<g opacity={placeholder}>
<ellipse cx={L.sim[0]} cy={L.sim[1]} rx={142} ry={102} fill="none" stroke="#3a4a72" strokeDasharray="7 7" />
<text x={L.sim[0]} y={L.sim[1] + 14} textAnchor="middle" fontSize={42} fill="#3a4a72">?</text>
</g>
)}
{grid > 0.01 && (
<g clipPath="url(#simClip)" opacity={grid * 0.6}>
{Array.from({ length: 13 }, (_, i) => (
<line key={"v" + i} x1={L.sim[0] - 138 + i * 23} y1={L.sim[1] - 102} x2={L.sim[0] - 138 + i * 23} y2={L.sim[1] + 102} stroke="#5fd4ff" strokeOpacity={0.13} />
))}
{Array.from({ length: 9 }, (_, i) => (
<line key={"h" + i} x1={L.sim[0] - 142} y1={L.sim[1] - 92 + i * 23} x2={L.sim[0] + 142} y2={L.sim[1] - 92 + i * 23} stroke="#5fd4ff" strokeOpacity={0.13} />
))}
</g>
)}
<Network cx={L.sim[0]} cy={L.sim[1]} t={t} color="#5fd4ff" alpha={simNet} square />
{simNet > 0.5 && (
<text x={L.sim[0]} y={L.simLabelY} textAnchor="middle" fontSize={12} fill="#8fa0c0">simulated brain — same causal structure</text>
)}
{/* step 1: physics compiles to bits */}
{physics > 0.01 && !L.vertical && (
<g opacity={physics}>
<text x={450} y={140} textAnchor="middle" fontSize={17} fontStyle="italic" fill="#ffd9a8" fontFamily="Georgia, serif">
{"\u2202\u03c8/\u2202t = \u0124\u03c8"}
</text>
<line x1={450} y1={152} x2={450} y2={188} stroke="#7ea0c8" strokeWidth={1.5} markerEnd="url(#arr)" />
{[0, 1, 2].map(k => (
<text key={k} x={450} y={212 + k * 19} textAnchor="middle" fontSize={12.5} fontFamily="ui-monospace, monospace" fill="#5fd4ff" opacity={0.85 - k * 0.22}>
{bitRow(k)}
</text>
))}
</g>
)}
{physics > 0.01 && L.vertical && (
<g opacity={physics}>
<text x={70} y={273} textAnchor="middle" fontSize={14} fontStyle="italic" fill="#ffd9a8" fontFamily="Georgia, serif">
{"\u2202\u03c8/\u2202t = \u0124\u03c8"}
</text>
<line x1={130} y1={268} x2={164} y2={268} stroke="#7ea0c8" strokeWidth={1.5} markerEnd="url(#arr)" />
{[0, 1].map(k => (
<text key={k} x={242} y={264 + k * 15} textAnchor="middle" fontSize={11.5} fontFamily="ui-monospace, monospace" fill="#5fd4ff" opacity={0.85 - k * 0.25}>
{bitRow(k)}
</text>
))}
</g>
)}
{/* step 2: one-to-one mapping lines */}
{mapLines > 0.01 && (
<g opacity={mapLines}>
{[0, 3, 6, 9, 12, 15].map(i => (
<line key={i}
x1={L.bio[0] + NODES[i][0]} y1={L.bio[1] + NODES[i][1]}
x2={L.sim[0] + NODES[i][0]} y2={L.sim[1] + NODES[i][1]}
stroke="#9aa7ff" strokeOpacity={0.5} strokeDasharray="3 5" />
))}
</g>
)}
{/* step 4: zombie hypothesis */}
{step === 4 && (
<g>
<text x={L.sim[0]} y={L.hypoY} textAnchor="middle" fontSize={13.5} fontStyle="italic" fill="#93a2c8" opacity={smooth(ts / 0.6)}>
hypothesis: no inner experience
</text>
<text x={L.sim[0]} y={L.tagY} textAnchor="middle" fontSize={12.5} fill="#5fd4ff" opacity={smooth((ts - 0.9) / 0.7)}>
…yet the report is identical
</text>
</g>
)}
{/* step 5: the hypothesis spreads to the biological brain */}
{step === 5 && (
<g>
<text x={L.sim[0]} y={L.hypoY} textAnchor="middle" fontSize={13.5} fontStyle="italic" fill="#93a2c8" opacity={0.8}>
hypothesis: no inner experience
</text>
<text x={L.anno[0]} y={L.anno[1]} textAnchor="middle" fontSize={13} fontStyle="italic" fill="#ffcf6e" opacity={smooth((ts - 0.4) / 0.6)}>
…the glow does no work now
</text>
<text x={L.vw / 2} y={L.tagY} textAnchor="middle" fontSize={12.5} fill="#93a2c8" opacity={smooth((ts - 2.4) / 0.8)}>
the words would be identical without it
</text>
</g>
)}
{/* step 6: the hypothesis is absurd; both glows return in tandem */}
{step === 6 && (
<g>
<text x={L.sim[0]} y={L.hypoY} textAnchor="middle" fontSize={13.5} fontStyle="italic" fill="#93a2c8" opacity={0.8}>
hypothesis: no inner experience
</text>
<line x1={L.sim[0] - 102 * strike} y1={L.hypoY - 4} x2={L.sim[0] + 102 * strike} y2={L.hypoY - 4} stroke="#ff5562" strokeWidth={3} opacity={strike} />
<text x={L.sim[0]} y={L.hypoY + 24} textAnchor="middle" fontSize={14} fontWeight={700} fill="#ff5562" opacity={strike}>
absurd
</text>
</g>
)}
{/* reports */}
{bubbles && (
<g>
<Bubble cx={L.bio[0]} top={L.bioBubbleTop} tail={!L.vertical} text={REPORT.slice(0, chars)} typing={typing && chars < REPORT.length} />
<Bubble cx={L.sim[0]} top={L.simBubbleTop} tail={!L.vertical} text={REPORT.slice(0, chars)} typing={typing && chars < REPORT.length} />
</g>
)}
{/* step 6: verdict stamp */}
{stamp > 0.01 && (
<g transform={`translate(${L.stamp[0]} ${L.stamp[1]}) scale(${stampScale * (L.vertical ? 0.78 : 1)})`} opacity={stamp}>
<rect x={-160} y={-44} width={320} height={88} rx={10} fill="#0d1220" fillOpacity={0.92} stroke="#3a4a72" />
<text x={0} y={-14} textAnchor="middle" fontSize={16} fontWeight={700} fill="#e8ecf6" letterSpacing="1">
BIOLOGICAL NATURALISM
</text>
<text x={0} y={8} textAnchor="middle" fontSize={12} fontStyle="italic" fill="#8fa0c0">
{"\u201cconsciousness requires biology\u201d"}
</text>
<g transform="rotate(-8)">
<rect x={-92} y={8} width={184} height={36} rx={5} fill="none" stroke="#ff5562" strokeWidth={3} />
<text x={0} y={34} textAnchor="middle" fontSize={21} fontWeight={800} fill="#ff5562" letterSpacing="2">
RULED OUT
</text>
</g>
</g>
)}
</svg>
<div className="caption">
<b>{STEPS[step].title}</b>
{STEPS[step].cap}
</div>
<div className="controls">
<button onClick={() => goTo(step - 1)} disabled={step === 0}>← Back</button>
<div className="dots">
{STEPS.map((_, i) => (
<button key={i} className={"dot" + (i === step ? " on" : "")} aria-label={`Step ${i}`} onClick={() => goTo(i)} />
))}
</div>
<button className="primary" onClick={() => goTo(step === STEPS.length - 1 ? 0 : step + 1)}>
{step === STEPS.length - 1 ? "↺ Restart" : "Next →"}
</button>
</div>
</div>
)
}
createRoot(document.getElementById("root")!).render(<App />)
```
**styles.css**
```css
html, body {
margin: 0;
background: #0d1220;
color: #e8ecf6;
}
.wrap {
height: 100dvh;
min-height: 480px;
box-sizing: border-box;
padding: 8px 10px;
font: 14px/1.5 system-ui, sans-serif;
display: flex;
flex-direction: column;
gap: 6px;
}
h1 { font-size: 30px; margin: 0; text-align: center; color: #c9d3e6; }
.sub { opacity: 0.65; margin-top: -2px; text-align: center; font-size: 18px; }
@media (max-width: 600px) {
h1 { font-size: 22px; }
.sub { font-size: 15px; }
.caption { font-size: 14px; min-height: 64px; }
}
svg.scene {
flex: 1;
min-height: 0;
width: 100%;
height: 100%;
display: block;
}
.caption {
width: 100%;
max-width: 760px;
margin: 0 auto;
box-sizing: border-box;
min-height: 76px;
padding: 8px 14px;
font-size: 15.5px;
color: #b9c3d8;
text-align: center;
}
.caption b { display: block; margin-bottom: 2px; color: #ccd5e8; }
.controls { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 10px; margin-bottom: 4px; }
button {
font: inherit;
padding: 6px 16px;
border: 1px solid currentColor;
border-radius: 8px;
background: transparent;
color: inherit;
cursor: pointer;
}
button.primary { font-weight: 600; }
button:disabled { opacity: 0.35; cursor: default; }
.dots { display: flex; gap: 7px; }
button.dot {
width: 10px; height: 10px;
padding: 0;
border-radius: 50%;
border: 1px solid currentColor;
opacity: 0.4;
}
button.dot.on { opacity: 1; background: currentColor; }
```
**index.html**
```html
<div id="root"></div>
```
**config.json**
```json
{
"description": "Animated step-through of the computability argument against biological naturalism: simulate a brain, ask it if it's conscious, and follow the logic.",
"dependencies": {
"react": "^19.2.7",
"react-dom": "^19.2.7"
}
}
```