---
format: typebulb/v1
name: Mobius Progress
---

**code.tsx**

```tsx
import React, { useMemo, useRef, useState } from "react";
import { createRoot } from "react-dom/client";
import { Canvas, useFrame, useThree } from "@react-three/fiber";
import { PerspectiveCamera } from "@react-three/drei";
import * as THREE from "three";

const STRIP_RADIUS = 1.8;
const STRIP_WIDTH = 0.5;
const ROT_SPEED = 0.5; // rad/sec
const START_ANGLE = -Math.PI / 2; // 6 o'clock position
const RUNNER_FOOT_LIFT = 0.46; // offset along surface normal so the feet sit on the strip

type RunnerMode = "walk" | "stand";

function mobiusPos(u: number, v: number) {
  const x = (STRIP_RADIUS + v * Math.cos(u / 2)) * Math.cos(u);
  const z = (STRIP_RADIUS + v * Math.cos(u / 2)) * Math.sin(u);
  const y = v * Math.sin(u / 2);
  return new THREE.Vector3(x, y, z);
}

function mobiusDerivatives(u: number, v: number) {
  // p(u,v):
  // x = (R + v cos(u/2)) cos u
  // z = (R + v cos(u/2)) sin u
  // y = v sin(u/2)
  const A = STRIP_RADIUS + v * Math.cos(u / 2);
  const dAdu = -0.5 * v * Math.sin(u / 2);

  const dxdu = dAdu * Math.cos(u) + A * -Math.sin(u);
  const dzdu = dAdu * Math.sin(u) + A * Math.cos(u);
  const dydu = 0.5 * v * Math.cos(u / 2);

  const dAdv = Math.cos(u / 2);
  const dxdv = dAdv * Math.cos(u);
  const dzdv = dAdv * Math.sin(u);
  const dydv = Math.sin(u / 2);

  return {
    dpdu: new THREE.Vector3(dxdu, dydu, dzdu),
    dpdv: new THREE.Vector3(dxdv, dydv, dzdv),
  };
}

const MobiusStrip = ({ dotsEnabled }: { dotsEnabled: boolean }) => {
  const materialRef = useRef<THREE.ShaderMaterial>(null);

  const geometry = useMemo(() => {
    const geo = new THREE.BufferGeometry();
    const vertices: number[] = [];
    const uVals: number[] = [];
    const vVals: number[] = [];
    const indices: number[] = [];
    const uSegments = 400;
    const vSegments = 40;

    for (let i = 0; i <= uSegments; i++) {
      const u = (i / uSegments) * Math.PI * 2;
      for (let j = 0; j <= vSegments; j++) {
        const v = (j / vSegments - 0.5) * STRIP_WIDTH;
        const x = (STRIP_RADIUS + v * Math.cos(u / 2)) * Math.cos(u);
        const z = (STRIP_RADIUS + v * Math.cos(u / 2)) * Math.sin(u);
        const y = v * Math.sin(u / 2);

        vertices.push(x, y, z);
        uVals.push(u);
        vVals.push(v);
      }
    }

    for (let i = 0; i < uSegments; i++) {
      for (let j = 0; j < vSegments; j++) {
        const row1 = i * (vSegments + 1);
        const row2 = (i + 1) * (vSegments + 1);
        const a = row1 + j;
        const b = row2 + j;
        const c = row2 + j + 1;
        const d = row1 + j + 1;
        indices.push(a, b, d);
        indices.push(b, c, d);
      }
    }

    geo.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));
    geo.setAttribute("uVal", new THREE.Float32BufferAttribute(uVals, 1));
    geo.setAttribute("vVal", new THREE.Float32BufferAttribute(vVals, 1));
    geo.setIndex(indices);
    geo.computeVertexNormals();
    return geo;
  }, []);

  const uniforms = useMemo(() => ({
    colorRed: { value: new THREE.Color(0xff2222) },
    colorBlue: { value: new THREE.Color(0x2222ff) },
    stripWidth: { value: STRIP_WIDTH },
    dotsEnabled: { value: dotsEnabled ? 1.0 : 0.0 }
  }), []);

  // Sync dotsEnabled state to uniform
  useFrame(() => {
    if (materialRef.current) {
      materialRef.current.uniforms.dotsEnabled.value = dotsEnabled ? 1.0 : 0.0;
    }
  });

  return (
    <mesh geometry={geometry}>
      <shaderMaterial
        ref={materialRef}
        side={THREE.DoubleSide}
        uniforms={uniforms}
        vertexShader={`
          varying float vU;
          varying float vV;
          attribute float uVal;
          attribute float vVal;
          void main() {
            vU = uVal;
            vV = vVal;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
          }
        `}
        fragmentShader={`
          varying float vU;
          varying float vV;
          uniform vec3 colorRed;
          uniform vec3 colorBlue;
          uniform float stripWidth;
          uniform float dotsEnabled;

          void main() {
            float u = vU;
            float phase = gl_FrontFacing ? 0.0 : 3.14159265;
            float t = cos(u * 0.5 + phase);
            vec3 color = mix(colorBlue, colorRed, t * 0.5 + 0.5);

            float s = vU / (6.28318530718);
            float w = (vV / stripWidth) + 0.5;
            float holeCount = 28.0;
            float ds = abs(fract(s * holeCount) - 0.5);

            // Two side-by-side holes per "station" so the runner can plausibly stay on the centerline.
            // Centers are offset from the middle, leaving a solid bridge at w ~= 0.5.
            float holeOffset = 0.23;     // separation from center (in normalized width 0..1)
            float holeHalfW = 0.14;      // hole radius across width
            float holeHalfU = 0.17;      // hole radius along u

            float dw1 = abs(w - (0.5 - holeOffset));
            float dw2 = abs(w - (0.5 + holeOffset));

            vec2 q1 = vec2(ds / holeHalfU, dw1 / holeHalfW);
            vec2 q2 = vec2(ds / holeHalfU, dw2 / holeHalfW);
            float d = min(length(q1), length(q2));
            float hole = 1.0 - smoothstep(1.0, 1.10, d);

            if (dotsEnabled > 0.5 && hole > 0.5) discard;
            gl_FragColor = vec4(color, 1.0);
          }
        `}
      />
    </mesh>
  );
};

const Runner = ({
  enabled,
  mode,
  rotYRef,
}: {
  enabled: boolean;
  mode: RunnerMode;
  rotYRef: React.MutableRefObject<number>;
}) => {
  const groupRef = useRef<THREE.Group>(null);
  const tRef = useRef(0);
  const prevModeRef = useRef<RunnerMode>(mode);
  const uTravelRef = useRef<number>(START_ANGLE); // used in "walk" mode (advances at ROT_SPEED to cancel group rotation)
  const uStandRef = useRef<number>(START_ANGLE); // used in "stand" mode (fixed on strip)

  const innerRef = useRef<THREE.Group>(null);
  const leftArmRef = useRef<THREE.Group>(null);
  const rightArmRef = useRef<THREE.Group>(null);
  const leftLegRef = useRef<THREE.Group>(null);
  const rightLegRef = useRef<THREE.Group>(null);
  const leftShinRef = useRef<THREE.Group>(null);
  const rightShinRef = useRef<THREE.Group>(null);

  const basisMat = useMemo(() => new THREE.Matrix4(), []);
  const quat = useMemo(() => new THREE.Quaternion(), []);
  const tmpRight = useMemo(() => new THREE.Vector3(), []);
  const tmpUp = useMemo(() => new THREE.Vector3(), []);
  const tmpForward = useMemo(() => new THREE.Vector3(), []);

  useFrame((state, delta) => {
    if (!enabled || !groupRef.current) return;

    // Initialize refs on first frame (ensures continuity even if rotY already advanced a bit)
    if (tRef.current === 0 && prevModeRef.current === mode) {
      uStandRef.current = START_ANGLE;
      uTravelRef.current = START_ANGLE;
    }

    // Handle mode transitions without teleporting:
    // - walk -> stand: freeze at the current evaluated u (uTravel - rotY)
    // - stand -> walk: pick uTravel so evaluated u stays the same after applying -rotY
    if (mode !== prevModeRef.current) {
      if (prevModeRef.current === "walk" && mode === "stand") {
        uStandRef.current = uTravelRef.current;
      } else if (prevModeRef.current === "stand" && mode === "walk") {
        uTravelRef.current = uStandRef.current;
      }
      prevModeRef.current = mode;
    }

    if (mode === "walk") {
      tRef.current += delta;
      uTravelRef.current += ROT_SPEED * delta;
    }
    // walk: advances u at ROT_SPEED so that (u - rotY) stays constant in world space
    // stand: stays on the same patch of strip (and rotates with it)
    const u = mode === "walk" ? uTravelRef.current : uStandRef.current;

    const v = 0; // centerline
    const pos = mobiusPos(u, v);
    const { dpdu, dpdv } = mobiusDerivatives(u, v);

    tmpForward.copy(dpdu).normalize();
    const widthDir = dpdv.clone().normalize();
    tmpUp.crossVectors(tmpForward, widthDir).normalize(); // surface normal (will flip after 1 loop)
    tmpRight.crossVectors(tmpUp, tmpForward).normalize();

    basisMat.makeBasis(tmpRight, tmpUp, tmpForward);
    quat.setFromRotationMatrix(basisMat);

    // Lift the runner so the feet (not the torso) sit on the surface
    pos.addScaledVector(tmpUp, RUNNER_FOOT_LIFT);

    groupRef.current.position.copy(pos);
    groupRef.current.quaternion.copy(quat);

    if (mode === "stand") {
      // Reset to a neutral pose (prevents freezing mid-stride when switching modes)
      if (leftLegRef.current) leftLegRef.current.rotation.x = 0;
      if (rightLegRef.current) rightLegRef.current.rotation.x = 0;
      if (leftShinRef.current) leftShinRef.current.rotation.x = 0;
      if (rightShinRef.current) rightShinRef.current.rotation.x = 0;
      if (leftArmRef.current) leftArmRef.current.rotation.x = 0;
      if (rightArmRef.current) rightArmRef.current.rotation.x = 0;
      if (innerRef.current) {
        innerRef.current.position.y = 0;
        innerRef.current.rotation.x = 0;
      }
      return;
    }

    // Cheap run-cycle (walk mode)
    const phase = tRef.current * 10.0;
    const swing = Math.sin(phase) * 0.85;
    const armSwing = -swing * 0.75;

    // Legs swing from hip
    if (leftLegRef.current) leftLegRef.current.rotation.x = swing;
    if (rightLegRef.current) rightLegRef.current.rotation.x = -swing;

    // Bend knee a bit on the "back" leg (simple heuristic)
    const leftKnee = Math.max(0, -Math.sin(phase)) * 1.1;
    const rightKnee = Math.max(0, Math.sin(phase)) * 1.1;
    if (leftShinRef.current) leftShinRef.current.rotation.x = leftKnee;
    if (rightShinRef.current) rightShinRef.current.rotation.x = rightKnee;

    // Arms swing opposite to legs
    if (leftArmRef.current) leftArmRef.current.rotation.x = armSwing;
    if (rightArmRef.current) rightArmRef.current.rotation.x = -armSwing;

    // Slight bob + forward lean
    if (innerRef.current) {
      innerRef.current.position.y = Math.sin(phase * 2.0) * 0.01;
      innerRef.current.rotation.x = 0.25; // lean forward
    }
  });

  if (!enabled) return null;

  return (
    <group ref={groupRef}>
      <group ref={innerRef}>
        {/* Torso */}
        <mesh position={[0, 0.02, 0]}>
          <capsuleGeometry args={[0.07, 0.22, 8, 12]} />
          <meshBasicMaterial color={0x2fbf71} />
        </mesh>

        {/* Pelvis */}
        <mesh position={[0, -0.12, 0]}>
          <boxGeometry args={[0.12, 0.08, 0.07]} />
          <meshBasicMaterial color={0x2f3a4a} />
        </mesh>

        {/* Neck */}
        <mesh position={[0, 0.17, 0]}>
          <cylinderGeometry args={[0.02, 0.02, 0.04, 10]} />
          <meshBasicMaterial color={0xffd2b8} />
        </mesh>

        {/* Head */}
        <mesh position={[0, 0.23, 0]}>
          <sphereGeometry args={[0.06, 18, 18]} />
          <meshBasicMaterial color={0xffd2b8} />
        </mesh>

        {/* Hair cap */}
        <mesh position={[0, 0.255, 0]}>
          <sphereGeometry args={[0.062, 18, 18, 0, Math.PI * 2, 0, Math.PI * 0.55]} />
          <meshBasicMaterial color={0x7a5a3a} />
        </mesh>

        {/* Arms (as pivoting groups at shoulders) */}
        <group ref={leftArmRef} position={[-0.095, 0.115, 0]}>
          {/* upper arm */}
          <mesh position={[0, -0.06, 0]}>
            <cylinderGeometry args={[0.018, 0.02, 0.12, 10]} />
            <meshBasicMaterial color={0xe9eef6} />
          </mesh>
          {/* forearm */}
          <mesh position={[0, -0.135, 0]}>
            <cylinderGeometry args={[0.016, 0.017, 0.10, 10]} />
            <meshBasicMaterial color={0xffd2b8} />
          </mesh>
          {/* hand */}
          <mesh position={[0, -0.19, 0]}>
            <sphereGeometry args={[0.018, 12, 12]} />
            <meshBasicMaterial color={0xffd2b8} />
          </mesh>
        </group>

        <group ref={rightArmRef} position={[0.095, 0.115, 0]}>
          <mesh position={[0, -0.06, 0]}>
            <cylinderGeometry args={[0.02, 0.018, 0.12, 10]} />
            <meshBasicMaterial color={0xe9eef6} />
          </mesh>
          <mesh position={[0, -0.135, 0]}>
            <cylinderGeometry args={[0.017, 0.016, 0.10, 10]} />
            <meshBasicMaterial color={0xffd2b8} />
          </mesh>
          <mesh position={[0, -0.19, 0]}>
            <sphereGeometry args={[0.018, 12, 12]} />
            <meshBasicMaterial color={0xffd2b8} />
          </mesh>
        </group>

        {/* Legs (hip pivot groups) */}
        <group ref={leftLegRef} position={[-0.045, -0.155, 0.015]}>
          {/* thigh */}
          <mesh position={[0, -0.065, 0]}>
            <boxGeometry args={[0.04, 0.13, 0.045]} />
            <meshBasicMaterial color={0x2f3a4a} />
          </mesh>
          {/* shin pivot at knee */}
          <group ref={leftShinRef} position={[0, -0.13, 0]}>
            <mesh position={[0, -0.06, 0.005]}>
              <boxGeometry args={[0.036, 0.12, 0.04]} />
              <meshBasicMaterial color={0x2f3a4a} />
            </mesh>
            {/* foot */}
            <mesh position={[0, -0.13, 0.035]}>
              <boxGeometry args={[0.05, 0.03, 0.09]} />
              <meshBasicMaterial color={0xf4b400} />
            </mesh>
          </group>
        </group>

        <group ref={rightLegRef} position={[0.045, -0.155, 0.015]}>
          <mesh position={[0, -0.065, 0]}>
            <boxGeometry args={[0.04, 0.13, 0.045]} />
            <meshBasicMaterial color={0x2f3a4a} />
          </mesh>
          <group ref={rightShinRef} position={[0, -0.13, 0]}>
            <mesh position={[0, -0.06, 0.005]}>
              <boxGeometry args={[0.036, 0.12, 0.04]} />
              <meshBasicMaterial color={0x2f3a4a} />
            </mesh>
            <mesh position={[0, -0.13, 0.035]}>
              <boxGeometry args={[0.05, 0.03, 0.09]} />
              <meshBasicMaterial color={0xf4b400} />
            </mesh>
          </group>
        </group>
      </group>
    </group>
  );
};

const Scene = ({
  dotsEnabled,
  runnerEnabled,
  runnerMode,
}: {
  dotsEnabled: boolean;
  runnerEnabled: boolean;
  runnerMode: RunnerMode;
}) => {
  const { viewport } = useThree();
  const tilt = Math.PI / 6; // 30 degrees
  const stripGroupRef = useRef<THREE.Group>(null);
  const rotYRef = useRef(0);

  const responsiveScale = useMemo(() => {
    // Math inspired by the Mobius Gear demo for perfect framing
    const outerR = STRIP_RADIUS + (STRIP_WIDTH / 2);
    const projectedW = outerR * 2.2;
    const projectedH = (STRIP_RADIUS * 2 * Math.cos(tilt)) + (STRIP_WIDTH * 2);

    const scaleX = viewport.width / projectedW;
    const scaleY = viewport.height / projectedH;
    return Math.min(scaleX, scaleY);
  }, [viewport.width, viewport.height, tilt]);

  useFrame((state, delta) => {
    rotYRef.current += delta * ROT_SPEED;
    if (stripGroupRef.current) stripGroupRef.current.rotation.y = rotYRef.current;
  });

  return (
    <group rotation={[tilt, 0, 0]} scale={responsiveScale}>
      <group ref={stripGroupRef}>
        <MobiusStrip dotsEnabled={dotsEnabled} />
        <Runner
          enabled={runnerEnabled}
          mode={runnerMode}
          rotYRef={rotYRef}
        />
      </group>
    </group>
  );
};

const App = () => {
  const [dotsEnabled, setDotsEnabled] = useState(true);
  const [runnerEnabled, setRunnerEnabled] = useState(true);
  const [runnerMode, setRunnerMode] = useState<RunnerMode>("walk");

  return (
    <div className="container">
      <Canvas dpr={[1, 2]} gl={{ antialias: true, alpha: true }}>
        {/* Orthographic-like perspective behavior */}
        <PerspectiveCamera makeDefault position={[0, 0, 50]} fov={5.5} />
        <ambientLight intensity={0.7} />
        <Scene
          dotsEnabled={dotsEnabled}
          runnerEnabled={runnerEnabled}
          runnerMode={runnerMode}
        />
      </Canvas>

      <div className="ui">
        <button className="toggle" onClick={() => setDotsEnabled(!dotsEnabled)}>
          {dotsEnabled ? "Holey" : "Smooth"}
        </button>
        <button className="toggle" onClick={() => setRunnerEnabled(!runnerEnabled)}>
          {runnerEnabled ? "With Man" : "Strip Only"}
        </button>
        <button
          className="toggle"
          onClick={() => setRunnerMode((m) => (m === "walk" ? "stand" : "walk"))}
          disabled={!runnerEnabled}
        >
          {runnerMode === "walk" ? "Walking" : "Standing"}
        </button>
      </div>
    </div>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(<App />);
```
**styles.css**

```css
:root {
  --bg-color: #050508;
  --ui-bg: rgba(255, 255, 255, 0.70);
  --ui-fg: rgba(0, 0, 0, 0.85);
  --ui-border: rgba(0, 0, 0, 0.18);
}

html[data-theme="dark"] {
  --ui-bg: rgba(0, 0, 0, 0.45);
  --ui-fg: rgba(255, 255, 255, 0.92);
  --ui-border: rgba(255, 255, 255, 0.16);
}

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: var(--bg-color);
}

#root {
  width: 100vw;
  height: 100vh;
}

.container {
  width: 100%;
  height: 100%;
  position: relative;
  background: radial-gradient(circle at center, #101015 0%, #050508 100%);
}

.ui {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 10;
  display: flex;
  flex-direction: row;
  gap: 8px;
}

.toggle {
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--ui-border);
  background: var(--ui-bg);
  color: var(--ui-fg);
  cursor: pointer;
  font-family: system-ui, -apple-system, sans-serif;
  font-size: 13px;
  font-weight: 500;
  backdrop-filter: blur(8px);
  transition: all 0.2s ease;
}

.toggle:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.toggle:hover {
  background: var(--ui-fg);
  color: var(--bg-color);
}

.toggle:active {
  transform: translateY(1px);
}

```
**index.html**

```html
<div id="root"></div>
```
**config.json**

```json
{
  "description": "Mobius progress - man walking the Möbius strip",
  "dependencies": {
    "three": "^0.165.0",
    "react": "^19.2.4",
    "react-dom": "^19.2.4",
    "@react-three/fiber": "^9.5.0",
    "@react-three/drei": "^10.7.7"
  }
}
```