---
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"
  }
}
```