# lantern > Animated, steppable code explainers rendered to SVG. Every step of every scene > has a URL, an SVG frame, a plain-text frame and a JSON trace of the state it explains. Base URL: https://lantern.ivar.workers.dev Browsers get the interactive player at /. Any client whose Accept header lacks text/html (curl, agents) gets this text at / ; it is also always at /llms.txt. ## Endpoints All GET, no auth, CORS open (access-control-allow-origin: *). - /index.json scene catalog: { scenes: [{ name, label }], help } - /index.json?scene=NAME { fps, duration, frames, steps: [{ name, path, t0, t1, frame0, frame1, meta }] } - /trace.json?scene=NAME { fps, duration, frames, steps: [{ step, t0, t1, meta, before, after, ... }] } before/after: the scene's state at each step boundary - /frame.svg?scene=NAME[&step=STEP | &t=SECONDS][&w=PX] one frame as image/svg+xml; w = width in px (default 1280) - /text?scene=NAME[&step=STEP | &t=SECONDS] the same frame as monospace text (text/plain) - /?scene=NAME&step=STEP the player, deep-linked (open in a browser; ?t=SECONDS also works) Params: - scene required except on /index.json; one of the names below - step a step name from /index.json; the frame at the moment that step begins - t seconds from the start; wins over step - (none) neither step nor t: the final frame ## Examples curl 'https://lantern.ivar.workers.dev/index.json' curl 'https://lantern.ivar.workers.dev/index.json?scene=lantern' curl 'https://lantern.ivar.workers.dev/text?scene=lantern&step=layout' curl 'https://lantern.ivar.workers.dev/frame.svg?scene=lantern&step=layout&w=640' -o frame.svg curl 'https://lantern.ivar.workers.dev/frame.svg?scene=lantern&t=3' -o frame.svg curl 'https://lantern.ivar.workers.dev/trace.json?scene=lantern' ## Errors JSON bodies, always with "error" and "help": "/llms.txt". - 404 unknown or missing scene: { "error": "unknown or missing scene", "scenes": [...names] } - 404 unknown step: { "error": "unknown step NAME", "steps": [...names] } - 400 bad t: { "error": "t must be a number of seconds" } - 400 bad w: { "error": "w must be a positive number of pixels" } ## Scenes - lantern: Lantern, drawn by Lantern (109.2 s) steps: source, layout, engine, sinks, surfaces, oracle, end - exgit: exgit: git from the inside (163.1 s) steps: intro, blob, tree, commit, history, refs, clone, end - just-bash: just_bash: a bash with no processes (148.9 s) steps: command, lex, parse, value, pipeline, lookup, redirect, result, limits, end - pyex-limits: pyex #29: cooperative resource limits (25.4 s) steps: before, ctx, boundaries, run, exceeded, cooperative - mr-cache: MR: KV cache in front of D1 (16.0 s) steps: before, diff, after, result - state-machine: State machine ↔ reducer (15.5 s) steps: intro, connect, opened, close, invalid - token-bucket: Token bucket rate limiter (26.3 s) steps: intro, t0, t0_5, t2_5 - glyph-text: Real text: outlines, Write, morph (8.7 s) steps: write, exact-boxes, morph, revert - ring-buffer: Ring buffer (JSON doc) (21.6 s) steps: intro, push-a, push-b, push-c, push-d, push-e, pop-a, pop-b, push-f, wrap, push-g, done ## Authoring (clone https://github.com/ivarvong/lantern; Node >= 22.18) A scene is a class extending Scene with an async construct(); await this.step(name, meta) marks steps, and this.state is what traces record. A scene can also be data: a JSON doc of nodes, edges, code and step ops, played by sceneFromDoc(doc) (docs/DOC-FORMAT.md). Register it in scenes/index.ts, then: npm run dev live player at http://localhost:5173/?scene=NAME npm run see -- NAME STEP a frame as text, plus the step's state diff npm run shot -- NAME contact sheet PNG: the end of every step npm run verify:motion -- NAME teleports, pops, cuts, flicker