Shapediver viewer
Agent skills for ShapeDiver
npx -y skills add shapediver/agent-skills --skill shapediver-viewerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use this skill when the user needs to write custom code using the ShapeDiver Viewer V3 API (@shapediver/viewer or the ShapeDiver CDN bundle) to build a 3D product configurator — including session/viewport setup, parameter controls, commit patterns, export downloads, React integration, and interaction features (selection, hover, drag, gumball, drawing tools, attribute visualization). Covers CDN and NPM workflows.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
27.0 KB, as published. Nobody here has run it
ShapeDiver Viewer V3 API
Prerequisite: This skill assumes you have already read and followed the
shapediver-routerskill. If you arrived here directly, stop — readshapediver-routerfirst. It selects the correct integration strategy and gathers required credentials before any implementation skill is read.
Follow every rule in this file exactly. Do not improvise or work around any constraint.
Scope discipline: Touch only what the user asked for. Do not add features, refactor adjacent code, or "improve" files beyond the stated request. If the user asks for a slider, build a slider — do not add export buttons, camera controls, or interaction features they did not request.
Workflow
Follow these steps in order. Do not skip steps or jump ahead to writing code. Each step has a checkpoint — do not proceed until the checkpoint is met.
Step 1: Collect Credentials and Model Metadata
Obtain ticket, modelViewUrl, and parameter/output/export metadata.
Use the API script (Option A below) or manual values from the user (Option B).
Checkpoint: You have real values for ticket and modelViewUrl, OR you have inserted
placeholders and asked the user to provide them. You have confirmed the AppBuilder output
check below.
Step 1a: AppBuilder Output Check (mandatory before writing any code)
Scan the outputs list from the script or user-provided metadata for an output named "AppBuilder" (exact name, case-insensitive). Do this before reading further.
If found: Stop. Ask the user:
"This model has an AppBuilder output — it is configured for the ShapeDiver App Builder. Are you sure you want a custom Viewer API integration instead? The App Builder may already cover your needs with less effort."
If they confirm custom integration, document their reason and continue.
If they want App Builder, hand off to the shapediver-appbuilder skill.
Checkpoint: You have confirmed no AppBuilder output exists, OR the user has explicitly confirmed they want a custom integration despite the AppBuilder output.
Step 2: Choose CDN or NPM
Ask the user's stack if not obvious. Use CDN + plain HTML for single-page demos, prototypes, and embeds. Use NPM + framework for production apps with build tooling. See "Setup" below.
Checkpoint: CDN or NPM decision is made. If CDN: you will use a single bundle.js
script tag (Rule 7). If NPM: you know the framework (React, Vue, plain TS).
Step 3: Scaffold the Page / Component
Create the HTML file or React component with the canvas element.
Checkpoint: The canvas element is always present in the DOM — not conditionally rendered (Rule 11). If showing a loading state, it is an overlay on top of the always-present canvas. The correct script tag (CDN) or imports (NPM) are in place.
Step 4: Create Viewport and Session
Write the initialization code: create viewport first, then session. Always await both.
See "Quick API Overview" below.
Checkpoint: createViewport is called before createSession. Both calls are awaited.
ticket and modelViewUrl are hardcoded from user-provided values — not exposed as UI
inputs (Rule 5). Error handling wraps initialization with getSDErrorMessage (Rule 8).
Step 5: Build Parameter Controls
Render UI controls for each parameter based on param.type. Filter out param.hidden,
sort by param.order, group by param.group.
Checkpoint — verify ALL of these before proceeding:
- Sliders (
Int,Float,Even,Odd): commit ononMouseUp/onChangeEnd, NOTonChange(Rule 1). React sliders useuseRefto avoid stale closures (Rule 3). - Color pickers: commit at end of interaction, not on every movement (Rule 2). Format is
0xRRGGBBAA(Rule 4). - Dropdowns (
StringList) and checkboxes (Bool): these are the ONLY controls whereonChangeis safe. - Text inputs (
String): commit ononBlur, NOTonChange(Rule 1). session.customize()is called explicitly after settingparam.value(Rules 9–10).- Values are formatted correctly per type — see "Parameter Formatting" reference.
- When multiple parameter types are requested, ALL must appear in the delivered file. A partial implementation is not acceptable — do not truncate or omit any requested control type. Prioritize complete code over explanatory prose.
Step 6: Add Interaction Features (if requested)
Only if the user asked for interactions (selection, drag, drawing tools, gumball, etc.). Load the specific reference file listed in "Interaction Features" below.
Checkpoint: Type guards are used before accessing param.settings (Rule 6). Settings
are extracted with param.settings?.props ?? param.settings — never read directly from
param.settings. The correct reference file has been read and its rules followed.
Step 7: Add Exports and Outputs (if requested)
Only if the user asked for file downloads or data output reading.
export.request() must be called explicitly — exports do not auto-trigger.
Checkpoint: Export requests use the correct API. Output data is read from
output.content. Error handling is in place.
Step 8: Add Cleanup
Add session.close() and viewport.close() in the appropriate cleanup path
(React useEffect return, window.onbeforeunload, etc.).
Checkpoint: Both session and viewport are closed on teardown. No dangling sessions.
Step 9: Review Against Critical Rules
Before delivering, re-read Critical Rules 1–12 below and verify the generated code against every one. Fix any violations.
Checkpoint — exit criteria (all must be true):
- Every Critical Rule (1–12) has been checked and the code complies.
- No model-specific values are invented — only user-provided or placeholder values are used.
- Credentials are not exposed in UI input fields.
- The code is a complete, runnable file — not a fragment.
Step 10: Serve Locally
If model metadata was retrieved via the API script (Option A), check the allowedDomains
array in the script output for a localhost entry (e.g., localhost:5000, 127.0.0.1:8080).
If a localhost domain with a specific port is found, serve the generated file on that port:
npx serve -l <port>
If no localhost entry exists in allowedDomains, inform the user that they need to add
their local development domain (e.g., localhost:3000) to the model's embedding domains
at https://www.shapediver.com/app/settings/domains before the viewer session will work
locally — otherwise session creation will fail with HTTP 403.
Checkpoint: The file is served on a port that matches a whitelisted domain, or the user has been informed about the domain whitelisting requirement.
Obtaining Model-Specific Values
Option A: Retrieve via API (preferred)
If the user provides a model slug (the URL identifier from shapediver.com/app/m/{slug})
and Platform API access keys (access key ID + secret), you can retrieve ticket,
modelViewUrl, and full parameter/output/export metadata automatically.
Run the shared script at the repository root (self-contained — auto-installs dependencies on first run, requires Node.js):
node ../../scripts/get-model-info.js <accessKeyId> <accessKeySecret> <slug>
Note: Adjust
../../scripts/to the actual path of the script relative to your current working directory. From a project at the workspace root, usescripts/get-model-info.js.
The script outputs clean JSON to stdout (diagnostics to stderr). The model object
contains two tickets and the model view URL:
ticket— the embedding ticket for Viewer use in the browser. Use this in generated frontend code.backendTicket— the backend ticket for headless/server-side SDK use.modelViewUrl— the Geometry Backend URL.
It also includes parameters, outputs, and exports. Run with --help for full usage and exit codes.
It performs a three-step flow:
- Authenticate with the ShapeDiver Platform Backend API (
POST /oauth/token) - Get model info by slug (
GET /api/v1/models/{slug}?embed=backend_ticket,backend_system) — returnsbackend_ticketandbackend_system.model_view_url - Init session on the Geometry Backend (
POST /api/v2/ticket/{ticket}) — returns all parameters, outputs, and exports with their IDs, types, defaults, etc. The session is closed immediately after retrieving the metadata.
Use the returned data to populate ticket, modelViewUrl, parameter names/IDs, etc. in
generated code.
Important: The model must have "backend access" enabled. The script will attempt to enable it automatically. Access keys are created at https://www.shapediver.com/app/settings/developers
After running the script: Before reading the full JSON, extract and review all output names to check for an AppBuilder output:
# Linux / macOS
cat model-info.json | node -e "const d=require('fs').readFileSync('/dev/stdin','utf8'); JSON.parse(d).outputs.forEach(o=>console.log(o.name))"
# Windows PowerShell
Get-Content model-info.json | node -e "const d=require('fs').readFileSync('/dev/stdin','utf8'); JSON.parse(d).outputs.forEach(o=>console.log(o.name))"
This reveals all output names in under a second and prevents missing a late-appearing AppBuilder output in a long JSON file.
Option B: Manual values from the user
If the user provides ticket and modelViewUrl directly (from the "Developers" tab on
shapediver.com), use those values as-is.
Placeholders — Never Invent Model-Specific Values
Use ONLY values the user has explicitly provided or that were retrieved via the API script. If missing, use the placeholder and ask.
| Value | Placeholder |
|---|---|
ticket | "PASTE_YOUR_TICKET_HERE" |
modelViewUrl | "PASTE_YOUR_MODEL_VIEW_URL_HERE" |
| Parameter name/ID | "PARAM_NAME_OR_ID" |
| Output name/ID | "OUTPUT_NAME_OR_ID" |
| Export name/ID | "EXPORT_NAME_OR_ID" |
When the user provides values (e.g., ticket: abc123 in the prompt): use them
exactly as-is. Do NOT add comments calling them placeholders or suggesting they need
to be replaced — they are the real values.
Do NOT filter or group parameters by guessing names. If grouping is needed, ask the user
for exact names/IDs. param.type tells you how to render a control, not which group it
belongs to.
Critical Rules
These are the most commonly violated patterns. Read all before writing code. For code examples of each rule, see references/core-patterns.md.
Rule 1: NEVER use onChange to commit parameters
Every session.customize() call is a network request. onChange on sliders, color pickers,
and text inputs fires continuously, causing HTTP 429 rate-limit errors.
Dropdown and checkbox are the ONLY controls where onChange is safe.
Use onMouseUp/onChangeEnd/onBlur for all others. See core-patterns.md § Commit Function.
Rule 2: Color picker — use debounced input + immediate change, not onChange or onMouseUp
onChange on <input type="color"> fires on every mouse movement — causes 429 errors.
This applies to both React (onChange) and vanilla JS — in both environments the event
fires continuously while the user drags. Do NOT use onChange (React) or oninput (DOM)
for color pickers, even indirectly through a wrapper function — if onChange triggers
any path that calls session.customize(), it violates this rule.
onMouseUp fires when opening the picker, not when selecting a color — wrong event.
For CDN / vanilla JS, use Pattern E2: add an input listener with a ~300 ms debounce
for live preview while dragging, plus a DOM change listener that clears the timer and
commits immediately when the picker closes. Both listeners are required — change-only
(Pattern E3) gives no live preview and feels unresponsive.
For React, onChange on <input type="color"> maps to the DOM change event (fires only
when the picker closes, not on every movement) — so onChange can be used for the final
commit. Pair it with a debounced onInput for live preview (Pattern E2).
See ui-patterns.md Pattern E for all implementations.
Rule 3: Slider — use useRef to avoid stale closures
React state is async. Reading state in onMouseUp captures the previous render's value.
See ui-patterns.md Pattern D.
Rule 4: Color format is 0xRRGGBBAA
<input type="color"> only accepts #RRGGBB. Convert with slice(2, 8), NOT slice(-6).
See parameter-formatting.md § Color.
Rule 5: Never expose credentials in UI
ticket and modelViewUrl are developer credentials. Hardcode them directly from what the
user provides. Never create input fields for end users to enter these values.
Rule 6: Check param.type before using type-specific APIs
Use type guards (isDrawingParameterApi, isSelectionParameterApi, etc.) before accessing
param.settings. Do not assume a type from the user's description.
Warning — Interaction param.settings is nested at runtime: The structure is
{ type: "selection", props: { nameFilter, maximumSelection, ... } }. The actual
properties are under settings.props, NOT directly on settings. Always extract:
const settings = param.settings?.props ?? param.settings;
Reading param.settings.nameFilter directly returns undefined.
Rule 7: CDN — single bundle.js, correct globals
- Use
https://viewer.shapediver.com/v3/latest/bundle.jswithcrossorigin="anonymous"(REQUIRED) - The global is
SDV, notSDV3 - Do NOT add extra
<script>tags or use esm.sh/unpkg/jsDelivr
Rule 8: Show actual errors, never generic messages
Use getSDErrorMessage(e) to extract the real error. See core-patterns.md § Error Extraction.
Rule 9: Do NOT enable customizeOnParameterChange
Leave it false (default). Call session.customize() explicitly after setting values.
Rule 10: Setting param.value does NOT send a request
Always call session.customize() after setting values.
Rule 11: Canvas MUST be in the DOM before createViewport
Never conditionally render the canvas. Use a loading overlay on top of an always-present canvas. See core-patterns.md § React Architectural Rules.
Rule 12: Live color preview — use client-side material mutation, NOT session.customize()
When the user wants live color preview without committing to the server immediately,
do NOT call session.customize() on every color picker movement — this causes 429 errors
and lag. Instead, mutate color on the existing MaterialStandardData objects in-place
using an output.updateCallback + viewport.update() pattern. Stage the value and commit
only when the user explicitly clicks Apply.
⚠️ Never replace a material object with new MaterialStandardData() — this discards
all texture maps and breaks the model's appearance. Always mutate the existing object.
Call updateVersion() on each mutated GeometryData and MaterialStandardData at the
leaf level (not just the output node) — geometry can be 6+ levels deep.
Register output.updateCallback so the color override persists after server-triggered
geometry updates (e.g., after clicking Apply).
See advanced-patterns.md Pattern L for the full implementation.
Anti-Rationalization Table
These are the shortcuts you will be tempted to take. Each one breaks the integration.
| You will think… | Why it is wrong |
|---|---|
"This is a simple demo, so onChange on the slider is fine." | Every customize() is a network request. Even a demo hits HTTP 429 errors within seconds of dragging a slider. Use onChangeEnd / onMouseUp. Always. |
| "I'll add a text field for the ticket so the user can test easily." | Tickets are credentials. Exposing them in a UI input trains users to paste secrets into form fields. Hardcode them from provided values. |
"I don't need settings.props — I'll read settings.nameFilter directly." | It returns undefined at runtime. The nested structure (settings.props) is how the API works. Always extract via param.settings?.props ?? param.settings. |
| "I know what parameter names this model has — I'll hardcode them." | You do not know. Parameter names are model-specific. Use placeholders or values from the API script. Never invent names. |
| "The user asked for a configurator, so I'll add export buttons too." | The user asked for what they asked for. Adding unrequested features violates scope discipline and bloats the code with untested behavior. |
"I'll use esm.sh / unpkg / jsDelivr for the CDN bundle." | The only supported CDN URL is https://viewer.shapediver.com/v3/latest/bundle.js. Third-party CDNs serve broken or incomplete builds. |
| "The code compiles, so it's correct — no need to re-check the rules." | Rules 1–4 are violated in the majority of LLM-generated ShapeDiver code. Step 9 of the Workflow exists because this rationalization is the most common failure mode. |
Setup
CDN vs. NPM decision: Use CDN + plain HTML for single-page demos, prototypes, and embeds. Use NPM + framework (React, Vue, etc.) for production apps with build tooling.
CDN (plain HTML)
<script
src="https://viewer.shapediver.com/v3/latest/bundle.js"
crossorigin="anonymous"
></script>
Use a fixed version (X.X.X) in production. This single script includes ALL features —
viewer, interactions, drawing tools, transformation tools, and attribute visualization.
Do NOT add extra <script> tags for individual features. Globals: SDV, SDVInteractions,
SDVDrawingTools, SDVTransformationTools, SDVAttributeVisualization.
CDN + React
window.SDV is not available at mount time. Use the loadShapeDiverCDN() loader from
advanced-patterns.md Pattern K — do NOT write your own.
NPM / React
import { createViewport, createSession } from "@shapediver/viewer";
For SSR (Next.js, Shopify Hydrogen), dynamic-import inside useEffect.
Feature Packages (NPM vs. CDN)
| Feature | NPM Package | CDN Global |
|---|---|---|
| Selection, Hover, Drag | @shapediver/viewer.features.interaction | SDVInteractions |
| Drawing Tools | @shapediver/viewer.features.drawing-tools | SDVDrawingTools |
| Gumball / Rectangle | @shapediver/viewer.features.transformation-tools | SDVTransformationTools |
| Attribute Visualization | @shapediver/viewer.features.attribute-visualization | SDVAttributeVisualization |
Quick API Overview
Create viewport first, then session. Always await both.
const viewport = await SDV.createViewport({
id: "vp",
canvas: canvasRef.current,
});
const session = await SDV.createSession({
id: "session",
ticket,
modelViewUrl,
});
session.automaticSceneUpdate = true;
Key APIs
- Parameters:
session.parameters(by ID),session.getParameterByName(name)(by name) - Outputs:
session.outputs,session.getOutputByName(name) - Exports:
session.exports,session.getExportByName(name),session.getExportByType(type)— must callexport.request(parameters?)explicitly; accepts optional{ [paramId]: value }overrides - Customize:
param.value = newVal; await session.customize(); - Sort params for UI:
Object.values(session.parameters).filter(p => !p.hidden).sort((a,b) => (a.order??0) - (b.order??0)) - Close:
session.close(); viewport.close();in cleanup
Parameter Properties
param.type— determines how to render a control and format the value:Bool,Int,Float,Even,Odd,String,StringList,Color,File,Drawing,Interactionparam.settings— pre-configured settings forDrawingandInteractionparams. Use type guards to determine the sub-type:isSelectionParameterApi(param),isDraggingParameterApi(param),isGumballTransformParameterApi(param),isRectangleTransformParameterApi(param),isDrawingParameterApi(param). Warning — For interaction parameters,settingsis nested at runtime:{ type: "selection", props: { nameFilter, maximumSelection, ... } }. Always extract:const settings = param.settings?.props ?? param.settings;param.group— model-author grouping from Grasshopper. Use this to organize UI sections. Do NOT guess groups from parameter names.param.visualization— hint for UI rendering (e.g.,CHECKLIST,SLIDER)param.order— sort order intended by the model authorparam.hidden— iftrue, do not show in UI
Some models also define dynamic parameters in their AppBuilder output that change based on model state. See references/dynamic-parameters.md.
For detailed API surfaces, load the specific reference needed:
- Session API (creation, customization, parameter/output access, model states, JWT, file uploads): references/session-api.md
- Viewport API (camera restrictions, environment, branding, screenshots, AR, rendering): references/viewport-api.md
- Parameter, Output & Export API (properties, type guards, export requests, output updates): references/parameter-output-export-api.md
- Scene Tree, Materials, Events, Animations, Three.js (ITreeNode, MaterialStandardData, event listeners, glTF): references/scene-tree-materials.md
- Advanced Topics (domain whitelisting, JWT, initial parameters, multi-session, performance): references/advanced-topics.md
For code patterns, load the specific reference needed:
- Parameter Formatting &
toSDValue()(Bool, Int, Float, StringList, Color, File conversion): references/parameter-formatting.md - Core Patterns (commitParam, error extraction, React architectural rules, commit events): references/core-patterns.md
- UI Patterns A–I (CDN setup, React setup, slider, dropdown, color picker, export, output, router): references/ui-patterns.md
- Advanced Patterns J–M & Troubleshooting (screenshot, CDN+React loader, live material override, file upload, error table): references/advanced-patterns.md
For advanced troubleshooting beyond the quick-reference table, see references/troubleshooting.md.
Interaction Features
When the user wants interaction features, load the specific reference:
- Name Filters (targeting specific nodes for interaction): references/name-filters.md
How
nameFilterpatterns work, dot-separated syntax, wildcards, scene tree traversal - Selection (click to select/deselect): references/interactions-selection.md SelectManager + HoverManager + InteractionEngine setup, select/deselect events, multi-select, cleanup/teardown, multiple selection parameters. Always includes hover — do not set up hover separately when using selection.
- Hovering (visual feedback on hover, standalone): references/interactions-hovering.md HoverManager setup for hover-only use cases. When combined with selection, hover is already included — see selection reference instead.
- Dragging (move objects by dragging): references/interactions-dragging.md DragManager + constraints. References selection.md for InteractionEngine
- Gumball Transform (3D translate/rotate/scale gizmo): references/gumball-transform.md GumballTransform setup, axis constraints, events
- Rectangle Transform (2D planar gizmo): references/rectangle-transform.md RectangleTransform setup, 2D bounds
- Drawing Tools (draw/edit points and lines): references/drawing-tools-reference.md Rules 10-12, settings tables, restrictions, full CDN + NPM examples, value format
- HTML Anchors (overlay HTML on 3D scene, in-scene UI): references/html-anchors.md Text labels, image anchors, custom data anchors, in-scene UI elements (buttons, controls, parameter widgets anchored to 3D objects)
- Attribute Visualization (color-code geometry by data): references/attribute-visualization.md Color-coded geometry overlays, layers, sdTF data inspection
- Post-Processing (visual effects on rendered scene): references/post-processing.md Bloom, SSAO, HBAO, Depth of Field, Outline, Vignette, Selective Bloom, custom effects, manual EffectComposer access, combining outline with interactions
- Animations (animate scene nodes): references/animations.md AnimationData + AnimationTracks, translation/rotation/scale keyframes, looping, glTF animations
- Augmented Reality (AR) (view models in real world): references/augmented-reality.md AR availability check, launch AR on device, QR code session links, scene setup for AR
- Three.js Objects (add custom three.js objects, access internal meshes): references/threejs-objects.md ThreejsData, adding Object3D to scene tree, accessing converted geometry, updateCallbackThreeJsObject
- glTF Loader (load external glTF/glb assets into the scene): references/gltf-loader.md DataEngine.loadContent(), external glTF enrichment, glTF preview tool