# Thin Ice An interactive lab that shows how an AI agent works. Claude has to guide an ice-fishing sled across a frozen lake to where the fish are biting, without going through the ice. It can't see the lake. All it can do is call five tools and read what comes back: - `look_around` reports what the surface looks like nearby. Surface hints can be wrong. - `measure_ice` drills a test hole here or next door and reports the thickness in inches. - `sonar` reports fish under the sled, and which direction reads stronger. - `move` moves the sled one cell. It refuses any cell that hasn't been measured at 4 inches or more. - `set_up_shanty` sets up camp. It only works where there are enough fish. ## The three modes 1. **Watch.** A small rule-based agent, written in JavaScript, crosses the lake while the messages, the loop code and the token count update in step. It reads only the transcript and answers in the same message format the Claude API uses. It is a stand-in so the page works without a key. 2. **You're the model.** The lake turns to fog. You get the system prompt, the tool definitions and the message history, and you write each turn yourself. 3. **Live with Claude.** Paste an Anthropic API key and the same loop calls the real Claude from your browser. The key stays in memory for that tab and goes only to api.anthropic.com. ## The files | File | What it does | | --- | --- | | `loop.js` | The agent loop. Call the model, run the tools it asks for, send the results back, repeat. | | `world.js` | The lake generator, the five tool definitions, the system prompt and the tool runner. | | `claude.js` | Live mode. Calls the Messages API through the Anthropic TypeScript SDK. | | `scripted.js` | Watch mode's rule-based stand-in. | | `scene.js` | The three.js scene. One custom shader draws the ice, cracks, water, drill holes, sonar and fog. | | `scene2d.js` | A lighter 2D view for devices without WebGL. Add `?lite` to the page address to see it. | | `app.js` | The page: modes, panels, captions and the concept tracker. | | `lab.css` | Styles for the lab. | ## The request Live mode sends ```js { model: "claude-opus-5-5", // or claude-sonnet-5-5, claude-haiku-5-5 max_tokens: 16000, system: SYSTEM_PROMPT, tools: TOOLS, // strict JSON schemas messages, // the whole history, every turn thinking: { type: "adaptive", display: "summarized" }, output_config: { effort: "low" }, cache_control: { type: "ephemeral" }, // Opus and Sonnet only: retry on Anthropic's fallback model if a request is declined betas: ["server-side-fallback-2026-07-01"], fallbacks: "default", } ``` ## Running it yourself The page is static. Bundle `app.js` with any ES module bundler that can resolve `three` and `@anthropic-ai/sdk` from npm, then serve the folder. Watch and You're the model need no key and make no network calls. Built by David Sapp with Claude Code.