One API. Built for humans and AI agents.
Embed a public share, wait for the viewer to become ready, then drive camera, materials, lighting, animation, screenshots, and saved states through one promise-first JavaScript API. Paste code from the Editor's API buttons into the playground below, or browse the complete reference.
For humans
Click an API button in the Editor to copy a ready-to-run snippet, paste it into the playground below, hit RUN, and tweak. The reference is a single scannable table.
For AI agents
Every method, event, and error code is on this page as plain text — no UI to click. A machine-readable mirror lives at /api/llms.txt. The SDK is JSON-only, namespaced, and stable within v1.
Quick Start
Use a public share slug. The SDK creates or binds an iframe, performs a version handshake, and returns a ready viewer object.
import { VoxoboxViewer } from 'https://voxobox.com/viewer-api/v1/index.js';
const viewer = await VoxoboxViewer.create({
iframe: '#viewer',
model: 'cqgyCF76b1kE',
});
await viewer.camera.fit({ target: 'model' });
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Voxobox Viewer</title>
<style>
body { margin: 0; min-height: 100vh; display: grid; place-items: center; background: #f7f8f8; }
#viewer { width: min(100vw, 960px); height: min(70vh, 560px); }
</style>
</head>
<body>
<div id="viewer"></div>
<script type="module">
import { VoxoboxViewer } from 'https://voxobox.com/viewer-api/v1/index.js';
const viewer = await VoxoboxViewer.create({
iframe: '#viewer',
model: 'cqgyCF76b1kE',
});
await viewer.camera.fit({ target: 'model' });
</script>
</body>
</html>
Playground
Paste any SDK code below — including snippets copied from the Editor's API buttons — and hit RUN. The viewer instance is pre-created and exposed as viewer, so you can skip VoxoboxViewer.create() and just write the calls.
How to use the Editor → Playground flow:
- Open the Editor, configure the menu you want (e.g. Lighting → Environment).
- Click the menu's API button to copy the snippet.
- Paste it below and hit RUN — the playground reuses the demo sphere so you see your settings applied immediately.
Ask AI For Code
Answer these questions, then copy the generated prompt into ChatGPT, Claude, Gemini, or another coding agent. The agent should return Playground-ready Voxobox code using the public SDK only.
Recipes By Editor Tab
These groups match the Editor's top-level tabs. Each group combines runnable code with the exact object shapes an AI agent needs when it never opens the Editor. Paste any snippet into the playground above; viewer is already available there.
API Reference
Every method on the v1 SDK, grouped by namespace. Reconciled against /viewer-api/v1/index.js. All methods return a promise and accept/return JSON-serializable values only. Each row has a stable anchor (e.g. #ref-camera-fit) for deep links.
viewer
Top-level methods (snapshots, batching, lifecycle).| Signature | Description |
|---|---|
| load(model, options?) | Load a different public share slug or https://voxobox.com/s/... share link into the existing viewer. Direct .glb/.gltf URLs are also accepted. Share loads do not restore saved Editor materials by default; pass { restoreMaterials: true } only when you explicitly want that saved material look. Add { restoreScene: true, restoreCamera: true } when restoring the Editor's saved camera/lights/background/post look too. |
| destroy() | Tear down the iframe and reject any pending calls. |
| getState() | Full serializable snapshot (camera, materials, lights, env, post). |
| applyState(state, options?) | Restore a snapshot previously returned by getState. |
| transaction(operations) | Apply a batch of { method, args } atomically. Preferred for agents. |
| configureVariant(variant) | Apply a high-level named variant declared on the share. |
| createTurntable(options?) | Auto-rotate the camera. Options: { speed?, axis?, pauseOnInteraction? }. |
viewer.camera
Orbit, framing, FOV, constraints.| Signature | Description |
|---|---|
| get() | Returns { eye, target } as Vec3s. |
| lookAt(eye, target, duration?) | Move the camera. Each of eye/target is [x, y, z]. Animates if duration > 0. |
| fit({ target?, name?, duration? }) | Frame the scene. target: 'model' | 'selection' | 'material' | 'node'; name required for material/node. |
| setConstraints(options) | Limit orbit/zoom range. |
| setFov(degrees) | Set vertical field of view. |
| getAngles() | Current azimuth/polar angles. |
| reset() | Restore the share's default camera. |
viewer.scene
Scene-level overlays.| Signature | Description |
|---|---|
| setWireframe({ enabled?, color?, opacity?, materialVisible? }) | Toggle the wireframe overlay and tune its color, opacity, and whether the underlying material stays visible. Passing a boolean is shorthand for { enabled }. |
| getRotation() | Current model rotation in degrees: { x, y, z }. |
| setRotation(x, y, z) | Set absolute model rotation in degrees. |
| rotateBy(axis, degrees) | Increment one axis ('x' | 'y' | 'z') by N degrees. Returns the new rotation. |
| resetRotation() | Snap model rotation back to { x: 0, y: 0, z: 0 }. |
viewer.materials
List, edit, swap textures.| Signature | Description |
|---|---|
| list() | Array of { name, uvCount, channels }. |
| get(name) | Full material descriptor, or null if not found. |
| update(name, channels) | Patch channels: { albedo: { color }, roughness: { value }, normal: { texture } }, etc. |
| replaceTexture(name, channel, texture) | Swap a single texture channel. Pass null to clear. |
| highlight(name, options?) | Briefly highlight a material for UI feedback. Options: { outline?, color?, strength?, thickness?, tintColor?, tintStrength?, tintIntensity?, fillColor?, fillStrength?, fillKeepTexture? }. Set outline: false to disable the edge outline. color, strength, and thickness control the outline. tintColor adds a temporary emissive/material tint. fillColor temporarily replaces the material fill color; by default it removes the albedo texture so the fill is solid. Use fillKeepTexture: true to keep the texture visible. All temporary highlight changes are restored by clearHighlight(). |
| clearHighlight() | Clear any active material highlight. |
viewer.nodes
Show, hide, focus scene nodes.| Signature | Description |
|---|---|
| list() | Flat array of scene nodes. |
| show(name) | Show a node. |
| hide(name) | Hide a node. |
| setVisible(name, visible) | Set visibility explicitly. |
| focus(name, options?) | Frame this node with the camera. |
viewer.selection
Hover/click selection state.| Signature | Description |
|---|---|
| get() | Current selection (material, node, point). |
| clear() | Clear the current selection. |
| pick(options) | Programmatically pick at a screen coordinate. |
viewer.annotations
Saved 3D notes and camera jumps.| Signature | Description |
|---|---|
| list() | Return all annotations. |
| set(annotations, settings?) | Replace annotations and optionally update settings. |
| add(annotation) | Add one annotation. Shape: { id?, position3D, title?, body?, color?, eye?, target? }. |
| update(id, patch) | Patch an annotation by id. |
| remove(id) | Remove an annotation by id. |
| clear() | Remove all annotations. |
| focus(id, options?) | Move the camera to the annotation's saved eye/target. Options: { duration? }. |
| getSettings() | Return annotation settings. |
| setSettings(settings) | Patch settings such as defaultColor, transitionSpeed, and dwellTime. |
viewer.environment
HDRI, exposure, tone mapping.| Signature | Description |
|---|---|
| get() | Current environment settings. |
| set({ hdri?, color?, fileName?, intensity?, blur?, rotation? }) | Set the environment from an HDRI URL or flat color. |
| setExposure(value) | Linear exposure multiplier. |
| setToneMapping(mode) | One of 'linear' | 'reinhard' | 'cineon' | 'aces' | 'agx'. |
| setFlat(color, intensity?) | Use a flat color instead of an HDRI. |
viewer.background
Visible canvas background.| Signature | Description |
|---|---|
| set({ color?, alpha?, image?, useEnvironment?, blur?, ... }) | Solid color, transparent, image, or use the environment as the background. |
viewer.lights
Manage scene lights and ground shadow.| Signature | Description |
|---|---|
| list() | Array of { index, type, position, intensity, color, ... }. |
| get(index) | Full descriptor for a single light. |
| update(index, patch) | Patch any light property. If the index does not exist yet, the viewer creates enough lights to make that index valid, then applies the patch. Use visible: false to hide. |
| add(descriptor) | Add a new light and apply the descriptor. Returns the created light descriptor. |
| remove(index) | Remove a light. |
| setType(index, type) | Convert a light to a different type. |
| aimAt(index, target) | Aim a directional or spot light at a point. |
| showHelpers(enabled) | Show or hide the editor-style light helpers in the viewer. |
| setGroundShadow({ enabled, mode?, intensity?, borderFade?, height?, shadowDiffusion?, bakedBlur?, environmentShadows?, fadeMode? }) | Toggle and tune the ground contact shadow. mode: 'shadow-catcher' | 'baked-ao'. fadeMode: 'model' | 'circle'. |
| getBakedShadow() | Return the current baked shadow texture (if any). |
viewer.post
Post-processing effects (per-effect setters).| Signature | Description |
|---|---|
| set(options) | Batch setter. Pass an object such as { bloom: {...}, ssao: {...} }; keys match the dedicated setters. |
| setSSR(params) | Screen-space reflections. |
| setSSAO(params) | Screen-space ambient occlusion. |
| setDOF(params) | Depth of field. |
| setBloom(params) | Bloom. |
| setGrain(params) | Film grain. |
| setVignette(params) | Vignette. |
| setChromatic(params) | Chromatic aberration. |
| setSharpness(params) | Sharpness / contrast-adaptive sharpening. |
| setToneMapping(params) | Tone-mapping curve override on the post pass. |
| setColorBalance(params) | Lift / gamma / gain color balance. |
| setAntialiasing(params) | Antialiasing mode (e.g. FXAA, TAA). |
viewer.animation
Playback control.| Signature | Description |
|---|---|
| list() | Available clips with names and durations. |
| play(indexOrName, options?) | Start a clip by index or name. Options: { loop?, speed? }. |
| pause() | Pause the active clip. |
| resume() | Resume the active clip. |
| stop() | Stop playback and reset time. |
| seek(seconds) | Jump to a time in seconds. |
| setSpeed(speed) | Playback speed multiplier. |
| setLoop(loop) | Toggle looping for the active clip. |
| getState() | Current playback state for all clips. |
viewer.screenshot
Capture a PNG.| Signature | Description |
|---|---|
| capture({ width?, height?, qualityScale?, format? }) | Returns { dataUrl, mimeType }. format defaults to 'png'. |
viewer.performance
Frame-rate and memory diagnostics.| Signature | Description |
|---|---|
| getReport() | Snapshot of FPS, draw calls, GPU memory, and other counters. |
viewer.events
Subscribe to viewer events.| Method / Event | Description |
|---|---|
| on(event, callback) | Subscribe. Returns an unsubscribe function. |
| off(event, callback) | Unsubscribe a specific callback. |
| viewer.ready | Emitted once after the version handshake completes. |
| model.progress | { pct } while the model loads. |
| model.loaded | Emitted once the model is fully loaded. |
| camera.changed | Emitted after camera movement settles. |
| selection.changed | Emitted when the hovered/picked material or node changes. |
| material.changed | Emitted after a successful materials.update. |
| animation.finished | Emitted when a non-looping clip completes. |
| light.moved | Emitted after a light position update. |
| error | Generic error channel; payload includes { code, message }. |
Errors
Rejected promises carry a stable string code alongside the human message. Agents should branch on code, not message. The codes currently emitted by the SDK and viewer controller:
VERSION_MISMATCH— the parent SDK and iframe RPC versions do not match.NOT_READY— the viewer is not ready to accept calls.TIMEOUT— the handshake or an individual call did not complete in time.CANCELLED— a call was rejected because the RPC client was destroyed (typically afterviewer.destroy()).NOT_FOUND— referenced node or animation does not exist.INVALID_METHOD— a method name is not part of the v1 contract.INVALID_ARGUMENTS— a method received unsupported arguments.ORIGIN_NOT_ALLOWED— this parent origin is not allowed for the share.RATE_LIMITED— too many calls in a rate-limited window, most commonly screenshots.VIEWER_ERROR— the iframe viewer failed while handling a valid call.
Agent Contract
Prefer high-level methods. Use viewer.transaction, viewer.configureVariant, viewer.camera.fit, viewer.getState, and viewer.applyState first. Drop to per-domain methods only when a task needs precise control.
Inputs and outputs are JSON. No DOM nodes, no Three.js objects, no functions cross the iframe boundary.
Stable surface. Within v1, method names, event names, and error codes do not change. Breaking changes ship as v2 at a new URL.
Discoverability. Fetch /api/llms.txt for the machine-readable mirror of this page, or scrape the API reference tables above — every td.sig cell is a signature.
await viewer.transaction([
{ method: 'materials.update', args: ['Fabric', { albedo: { color: '#334155' } }] },
{ method: 'camera.fit', args: [{ target: 'material', name: 'Fabric', duration: 0.35 }] }
]);