# Driving Unroll as an agent Unroll is a static page. Everything it does is reachable without touching the mouse: through the JavaScript API at `window.unroll`, through URL parameters, or by keyboard. The full machine-readable reference is [`/api.json`](api.json); `await unroll.schema()` returns the same list at run time, including add-ons such as `unroll.bench`. ## Rules of the API - Every method returns a Promise of `{ ok: true, ... }` or `{ ok: false, error, code }`. Nothing throws. - Arguments are validated; errors say what was wrong and what is allowed. - Codes: `invalid`, `not_found`, `unavailable` (not in this build or this scroll), `timeout`, `conflict`, `failed`, `load_failed`. - Screen coordinates (Destroy tools) are CSS pixels in the viewport. Scene coordinates (camera) are millimeters. - If the scroll fails to load, `window.unroll` still offers `getState`, `version`, `openScroll` and `reload`. ## Start ```js await unroll.waitFor('ready'); // the scroll is loaded const s = await unroll.getState(); // scroll, t, layer, winding, focus, panels, destroy, camera, description, url await unroll.describe(); // the current view in words await unroll.version(); // { version, date, sha, api } ``` ## Common tasks | Task | Call | | --- | --- | | Unroll to a position | `setUnroll(0.5)` then `waitFor('settled')` | | Help and schema | `help()`, `help('destroy')`, `help('setLayer')`, `schema()` | | Animate | `play(true)`, `play(false)` | | Switch layer | `setLayer('xray' \| 'ink' \| 'overlay')` | | Close-up of a winding | `focus('w047', { view: 'overlay' })`; close with `focus(null)` | | Compare swipe | `compare(true, 0.3)` | | Zoom and pan the close-up | `closeup({ scale: 2, x: 40, y: 0 })`, `closeup({ fit: true })` | | Panels | `panel('about' \| 'help' \| 'open' \| 'why', true/false)` | | Camera | `camera.get()`, `camera.orbit(deg, deg)`, `camera.zoom(f)`, `camera.pan(dx, dy)`, `camera.set([x,y,z],[x,y,z])`, `camera.reset()` | | Destroy | `destroy.mode(true)`, `destroy.tool('burn')`, `destroy.aim()` (a point on the papyrus), `destroy.tear(x, y, { dx, dy })`, `destroy.crush(x, y, { hold })`, `destroy.burn(x, y)`, `destroy.water(x, y, { hold })`, `destroy.crumble()`, `destroy.reset()`, `destroy.stats()`, `destroy.tools()` | | Destroy Sandbox | `destroy.sandbox(true)`, `destroy.grab(x, y, { dx, dy })`, `destroy.shake()` | | Roll it back up | `rollUp(true)`, `rollUp(false)` | | Sound, fullscreen | `sound(true)`, `fullscreen(true)` | | Image of the view | `screenshot()` gives `{ dataURL }` (PNG) | | A keyboard shortcut | `key('2')`, `key('Space')` | | Other scrolls | `listScrolls()`, `openScroll(id)` (the page reloads), `prizeOnly(true)` (filter the picker to prize eligible scrolls) | | Settings and the control panel | `ui.settings()`, `ui.setSetting('quality', 'low')`, `ui.resetSettings()`, `ui.settingsPanel(true)`, `ui.moreControls(true)`, `ui.skipIntro()`, `ui.glossary('voxel')`, `ui.glossarySearch('patch')`, `ui.actions()`, `ui.action('tool.burn')` | | Ink Bench | `bench.open('compare')`, `bench.compare({ segment, layer, thresholds })`, `bench.metrics()`, `bench.close()`; see docs/BENCH.md | | Open your own | `open.fromURL(url)`, `open.fromFiles([{ name, data }])` with an ArrayBuffer, Uint8Array, Blob or base64 string; `open.example()`, `open.validate(manifest)`, `open.listLocal()`, `open.removeLocal(key)` | | Share the view | `deepLink(true)` | Tool calls take screen points; a point that is not on the papyrus answers `{ ok: false, code: 'not_found' }`. `destroy.aim()` returns one that is. ## Waiting and events ```js await unroll.waitFor('settled'); // unroll and camera at rest await unroll.waitFor({ layer: 'xray', t: { gte: 0.5 } }, { timeout: 5000 }); const { id } = await unroll.on('change', (e) => console.log(e.changed)); await unroll.off(id); ``` Events: change, unroll, layer, focus, panel, destroy, camera, settled, scroll, extension, error. ## Batches ```js await unroll.run([ ['setLayer', 'overlay'], ['focus', 'w047', { view: 'compare' }], ['compare', true, 0.4], ['screenshot'], ], { continueOnError: false }); ``` ## URL parameters `scroll` (alias `s`), `t`, `layer` (alias `l`), `focus`, `view`, `compare`, `panel`, `destroy`, `tool`, `crush`, `sandbox`, `cam`. They work in the query string or the hash (the hash wins), and the page keeps the hash up to date. ## Selectors for browser automation Every interactive element has a stable `data-testid` and a `data-api` attribute naming its API equivalent, for example `