# API (/docs/api) The vitrine API lets you manage models, configure scenes, and generate embed codes from any HTTP client — your own scripts, CI pipelines, or AI agents. ## Base URL [#base-url] ``` https://api.vitrine3d.com ``` ## Authentication [#authentication] ### API Key [#api-key] Generate a key in the [dashboard](https://app.vitrine3d.com) under Account → API Keys. Include it in the `Authorization` header: ``` Authorization: Bearer vt_abc123... ``` ### Supabase JWT [#supabase-jwt] If you already have a Supabase session (e.g. from the dashboard), you can use the JWT directly. The API accepts both API keys and JWTs. ### Anonymous [#anonymous] If no credentials are provided on a write request, the API creates an anonymous session automatically. Anonymous uploads expire after 48 hours. Link them to a permanent account by signing up. ## Quick Start [#quick-start] List your models: ```bash curl https://api.vitrine3d.com/v1/models \ -H "Authorization: Bearer vt_abc123..." ``` Upload a GLB file: ```bash curl -X POST https://api.vitrine3d.com/v1/models \ -H "Authorization: Bearer vt_abc123..." \ -F file=@shoe.glb \ -F name="Running Shoe" ``` Configure the scene: ```bash curl -X PATCH https://api.vitrine3d.com/v1/models/MODEL_ID/config \ -H "Authorization: Bearer vt_abc123..." \ -H "Content-Type: application/json" \ -d '{ "bg_mode": "transparent", "hdri_preset": "studio_small_08_4k", "bloom_enabled": true, "bloom_intensity": 0.15 }' ``` Publish and get embed code: ```bash curl -X PATCH https://api.vitrine3d.com/v1/showcases/MODEL_ID \ -H "Authorization: Bearer vt_abc123..." \ -H "Content-Type: application/json" \ -d '{"published": true}' curl https://api.vitrine3d.com/v1/showcases/MODEL_ID/embed \ -H "Authorization: Bearer vt_abc123..." ``` ## Endpoints [#endpoints] ### Models [#models] | Method | Path | Description | | -------- | ---------------- | -------------------------------- | | `GET` | `/v1/models` | List your models | | `POST` | `/v1/models` | Upload a GLB file (multipart) | | `GET` | `/v1/models/:id` | Model details with merged config | | `DELETE` | `/v1/models/:id` | Delete model and storage | ### Config [#config] | Method | Path | Description | | ------- | ----------------------- | --------------------------------------------------- | | `GET` | `/v1/models/:id/config` | Get merged scene config (look + layout + overrides) | | `PATCH` | `/v1/models/:id/config` | Apply config patch | ### Showcases [#showcases] | Method | Path | Description | | ------- | ------------------------- | -------------------------------------------- | | `PATCH` | `/v1/showcases/:id` | Update name, publish status, look, or layout | | `GET` | `/v1/showcases/:id/embed` | Get embed HTML for a published model | ### Looks [#looks] | Method | Path | Description | | -------- | --------------- | --------------------------- | | `GET` | `/v1/looks` | List your looks | | `POST` | `/v1/looks` | Create a look from a config | | `PATCH` | `/v1/looks/:id` | Update look name or config | | `DELETE` | `/v1/looks/:id` | Delete look | ### Thumbnails [#thumbnails] | Method | Path | Description | | ------ | -------------------------- | ------------------------------------ | | `POST` | `/v1/models/:id/thumbnail` | Trigger server-side thumbnail render | ### Feedback [#feedback] | Method | Path | Description | | ------ | -------------- | -------------------------------------- | | `POST` | `/v1/feedback` | Submit a bug report or feature request | ### Account [#account] | Method | Path | Description | | -------- | -------------------------- | ----------------------------- | | `GET` | `/v1/account` | Current user, plan, and usage | | `POST` | `/v1/account/api-keys` | Generate a new API key | | `DELETE` | `/v1/account/api-keys/:id` | Revoke an API key | ### Reference (no auth required) [#reference-no-auth-required] | Method | Path | Description | | ------ | ------------------- | --------------------------- | | `GET` | `/v1/hdris` | List available HDRI presets | | `GET` | `/v1/config-schema` | Scene config JSON schema | ## Config Patch Format [#config-patch-format] Only include fields you want to change. The patch is merged on top of the existing config: ```json { "bg_mode": "transparent", "bloom_enabled": true, "bloom_intensity": 0.15, "hdri_preset": "studio_small_08_4k", "auto_rotate": true } ``` Use `GET /v1/config-schema` to see all available fields, types, enums, and defaults. ## Rate Limits [#rate-limits] | Auth mode | Limit | | -------------- | ---------------- | | Anonymous | 20 requests/min | | API Key (Free) | 60 requests/min | | API Key (Pro) | 300 requests/min | Rate limit status is returned in the `X-RateLimit-Remaining` header. ## Errors [#errors] All errors return a JSON body with `error` and `message` fields: ```json { "error": "model_not_found", "message": "No model found with ID 'abc123'" } ``` | Status | Meaning | | ------ | ------------------------------------------------- | | 400 | Invalid request (bad config patch, missing field) | | 401 | Not authenticated | | 403 | Not authorized (wrong owner, plan limit reached) | | 404 | Resource not found | | 429 | Rate limited — check `Retry-After` header | # Embed Reference (/docs/embed) ## Basic embed [#basic-embed] Add the script once per page, then place `
` elements wherever you want a viewer: ```html
``` The viewer fills its container. Use CSS to control dimensions — `width`, `max-width`, `height`, and `aspect-ratio` all work. ## Attributes [#attributes] | Attribute | Type | Description | | ----------------- | --------- | ----------------------------------------------------------------------- | | `data-vitrine` | flag | Required. Marks the element as a viewer. | | `data-vitrine-id` | string | Showcase ID from the dashboard. Loads the model, config, and thumbnail. | | `data-model` | URL | Direct GLB/glTF URL (alternative to showcase ID). | | `data-config` | JSON | Inline config patch as JSON string. Overrides showcase defaults. | | `data-poster` | `"false"` | Set to `"false"` to disable the loading thumbnail. | ### Using `data-config` [#using-data-config] You can override any scene setting inline: ```html
``` This is useful for showing the same model with different settings on the same page (e.g., different background colors for each variant). ## JavaScript API [#javascript-api] The global `Vitrine` object provides two methods: ### `Vitrine.boot(element)` [#vitrinebootelement] Manually boots a viewer element. Normally viewers auto-boot on page load and when new elements appear (via MutationObserver). Use this if you add elements dynamically and need immediate control. ```js const el = document.createElement('div'); el.dataset.vitrine = ''; el.dataset.vitrineId = 'abc123'; el.style.cssText = 'width:100%;aspect-ratio:16/9'; document.body.appendChild(el); Vitrine.boot(el); ``` ### `Vitrine.update(element, config)` [#vitrineupdateelement-config] Pushes a config patch to a running viewer. Use this for variant switching, interactive controls, or scroll-driven effects. ```js // Switch background color Vitrine.update(el, { bg_color: [0.1, 0.1, 0.1, 1.0] }); // Toggle bloom Vitrine.update(el, { bloom_enabled: true, bloom_intensity: 0.15 }); ``` The config object accepts the same fields as `data-config`. ## Multiple viewers [#multiple-viewers] You can place multiple viewers on the same page. Each gets its own WASM instance: ```html
``` Viewers boot sequentially to avoid overwhelming the GPU. Each viewer only renders when visible (IntersectionObserver) and uses zero GPU when idle. ## Sizing [#sizing] The viewer is not an iframe — it participates in your page layout directly. Set dimensions with CSS: ```css /* Fill container width, fixed aspect ratio */ [data-vitrine] { width: 100%; aspect-ratio: 4/3; } /* Fixed size */ [data-vitrine] { width: 600px; height: 450px; } /* Responsive with max-width */ [data-vitrine] { width: 100%; max-width: 800px; aspect-ratio: 16/9; } ``` ## Lazy loading [#lazy-loading] Viewers only boot when they enter the viewport (or are close to it). No extra configuration needed — this is the default behavior. Thumbnail posters display immediately for showcases that have them. ## Rendering backend [#rendering-backend] The viewer automatically selects **WebGPU** when available and falls back to **WebGL2** otherwise. See [WebGPU Support](/docs/webgpu) for details on browser support and troubleshooting. # Supported Formats (/docs/formats) vitrine supports **GLB** and **glTF 2.0** files — the standard formats for real-time 3D on the web. ## GLB vs glTF [#glb-vs-gltf] | Format | Extension | Description | | ------ | --------------------------- | ----------------------------------------------------------------------------------------- | | GLB | `.glb` | Single binary file containing geometry, textures, and materials. Recommended. | | glTF | `.gltf` + `.bin` + textures | Separate files. Upload as a single `.gltf` file (external references must be accessible). | **Use GLB** whenever possible. It's a single file with everything embedded, which means faster uploads and fewer issues with missing textures. ## Supported features [#supported-features] vitrine supports the full glTF 2.0 PBR material model: * **PBR metallic-roughness** materials * **Normal maps**, occlusion maps, emissive maps * **Alpha blending and alpha cutoff** (transparent/translucent materials) * **Double-sided** materials * **Multiple UV sets** * **Vertex colors** * **KHR\_materials\_unlit** extension * **KHR\_texture\_transform** extension ## Optimization tips [#optimization-tips] ### Polygon count [#polygon-count] There is no hard polygon limit, but keep performance in mind: | Use case | Suggested range | Notes | | ----------------------- | ------------------- | ---------------------------------------------- | | Product page embed | 50k–200k triangles | Fast load, smooth on mobile | | Hero showcase | 200k–500k triangles | Fine on desktop, may stutter on low-end mobile | | Portfolio / detail view | 500k–1M triangles | Desktop-focused, expect slower mobile | Use your 3D software's decimate/simplify tools to reduce polygon count without visible quality loss. ### Textures [#textures] * **Stick to power-of-two resolutions** (1024x1024, 2048x2048) — GPUs handle these most efficiently * **2048x2048 is usually enough** for product viewers. 4K textures rarely add visible detail at typical embed sizes * **Compress textures** before embedding them in the GLB. Use JPEG for color maps, PNG only when you need alpha * **Use KTX2/Basis** compression if your export tool supports it — smaller files, faster loads ### File size [#file-size] * Under **10 MB** is ideal for fast loading * **10–30 MB** is acceptable for hero viewers with thumbnail posters * Over **30 MB** will cause slow loads on mobile connections ### Tools for optimization [#tools-for-optimization] * **[gltf-transform](https://gltf-transform.dev)** — CLI for compressing, resizing textures, and optimizing glTF files * **[Blender](https://www.blender.org)** — decimate modifier, texture baking, GLB export * **[RapidPipeline](https://rapidpipeline.com)** — automated 3D optimization (commercial) ## Model orientation [#model-orientation] vitrine handles orientation automatically. Your model can use any up-axis convention (Y-up, Z-up) — the viewer maps it to the correct orientation on load. If the result looks wrong, you can adjust the orientation mapping in the editor. ## Where to find models [#where-to-find-models] Looking for 3D product models to try? * **[Poly Haven](https://polyhaven.com/models)** — free CC0 models (furniture, props, decor) * **[Sketchfab](https://sketchfab.com)** — large marketplace with free and paid models * **[Khronos glTF Samples](https://github.com/KhronosGroup/glTF-Sample-Assets)** — official test models * **[Quaternius](https://quaternius.com)** — free low-poly game assets # Getting Started (/docs/getting-started) ## 1. Upload a model [#1-upload-a-model] Open the [vitrine dashboard](https://app.vitrine3d.com) and drag a `.glb` or `.gltf` file onto the page. No account required — you can try it anonymously. Your model appears in the editor immediately. Don't have a model? Try a free one from [Poly Haven](https://polyhaven.com/models), [Sketchfab](https://sketchfab.com/search?type=models\&licenses=7c23a1ba438d4306920229c12afcb5f9), or the [Khronos glTF samples](https://github.com/KhronosGroup/glTF-Sample-Assets). ## 2. Style the scene [#2-style-the-scene] Use the editor sidebar to configure: * **Environment** — pick an HDRI preset for lighting and reflections * **Background** — solid color, transparent, or environment skybox * **Lights** — key, fill, and rim lights with color and intensity * **Effects** — bloom, tonemapping, depth of field (WebGPU) Changes apply in real-time. When you're happy with the look, hit **Save**. ## 3. Configure camera & embed [#3-configure-camera--embed] Switch to the **Publish** tab to set up the viewer behavior: * **View Origin** — starting zoom, pitch, and yaw * **Camera Interaction** — orbit mode, zoom constraints * **Embed** — poster image, loading strategy, watermark ## 4. Get the embed code [#4-get-the-embed-code] Copy the embed snippet — it looks like this: ```html
``` Paste it into your HTML. The viewer loads automatically. ## 5. Optional: create an account [#5-optional-create-an-account] Anonymous uploads expire after 48 hours. To keep your models permanently: * **Sign up** — your anonymous models are automatically claimed * **Log in** (if you already have an account) — models are transferred to your account You can sign up with email or Google. ## Next steps [#next-steps] * [Embed Reference](/docs/embed) — all available attributes and the JS API * [Looks & Layouts](/docs/looks-and-layouts) — reuse styles across multiple models * [Supported Formats](/docs/formats) — model requirements and optimization tips # Welcome (/docs) vitrine lets you embed photorealistic, interactive 3D viewers on your product pages. Upload a GLB model, style the scene, and drop it into any site — no iframes, no plugins, no build step. ## How it works [#how-it-works] 1. **Upload** a 3D model (GLB/glTF) in the [dashboard](https://app.vitrine3d.com) 2. **Style** the scene — pick lighting, background, camera angle, and effects 3. **Embed** with a single script tag and a `
` element The viewer runs in the browser using WebAssembly with WebGPU (or WebGL2 fallback), giving you real-time PBR rendering with environment lighting, bloom, depth of field, and more. ## Key features [#key-features] * **One script tag** — no iframe sandbox, your CSS controls sizing * **Looks & Layouts** — style once, apply to every product * **WebGPU + WebGL2** — automatic fallback, works in all modern browsers * **Reactive rendering** — zero GPU cost when idle, battery-friendly * **Shopify integration** — theme extension for automatic 3D on product pages * **Thumbnail posters** — instant loading with server-rendered placeholders ## Next steps [#next-steps] # Looks & Layouts (/docs/looks-and-layouts) Looks and Layouts let you define reusable viewer styles and apply them to multiple models. Change a Look once, and every viewer using it updates automatically. ## What is a Look? [#what-is-a-look] A **Look** controls how the scene appears: * Environment (HDRI preset) * Background mode and color * Lighting setup (key, fill, rim lights) * Effects (bloom, tonemapping, ambient occlusion) Think of it as a "theme" for your 3D viewers. A jewelry store might have a dark, dramatic Look. A furniture brand might use a bright, neutral one. ## What is a Layout? [#what-is-a-layout] A **Layout** controls the camera and embed behavior: * Camera mode (turntable or walk-around) * Starting view (zoom, pitch, yaw) * Interaction constraints (orbit limits, zoom mode) * Aspect ratio and viewer sizing Layouts are separate from Looks so you can use the same visual style at different sizes (e.g., a square thumbnail vs. a widescreen hero). ## Creating a Look [#creating-a-look] 1. Open any model in the [editor](https://app.vitrine3d.com) 2. Configure the scene until you're happy with it 3. Click **Save as Look** in the settings panel 4. Name it (e.g., "Studio Dark", "Product White") ## Per-model overrides [#per-model-overrides] Sometimes one product needs a small tweak — more bloom, a different camera angle — while keeping everything else from the Look. In the editor, change any setting on a per-model basis. Only the fields you change are stored as overrides. The rest still inherit from the Look. The editor shows which settings are overridden with a visual indicator. Click the revert button to remove an override and fall back to the Look's default. ## How inheritance works [#how-inheritance-works] ``` Look (base style) └── Model override (sparse patch) └── Embed override (data-config inline) ``` Each layer only stores the fields it changes. Values cascade: embed overrides take priority, then model overrides, then the Look defaults. This means you can update a Look (e.g., change the HDRI) and it cascades to all models that use it — except for fields those models have explicitly overridden. # MCP Server (/docs/mcp) The vitrine MCP server lets AI agents manage your 3D viewers directly. Upload models, configure scenes, generate embeds — all through natural language. ## What you can do [#what-you-can-do] Ask your AI agent things like: * "Upload this shoe model and give me an embed code" * "Make the lighting warmer and add bloom" * "List my models and publish the chair" * "Create a dark Look called 'Noir' and apply it to all my products" * "File a bug: the viewer flickers on Safari" ## Setup [#setup] Add vitrine to your MCP client config. The config file location depends on your client: * **Claude Code:** `.mcp.json` in your project, or `~/.claude/settings.json` * **Claude Desktop:** `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows) * **Cursor:** `.cursor/mcp.json` in your project ### With API key (recommended) [#with-api-key-recommended] Generate a key in the [dashboard](https://app.vitrine3d.com) under Account → API Keys, then add: ```json { "mcpServers": { "vitrine": { "command": "npx", "args": ["@vitrine3d/mcp"], "env": { "VITRINE_API_KEY": "vt_abc123..." } } } } ``` ### Without setup (anonymous) [#without-setup-anonymous] Omit the `env` block to use anonymous mode. Models expire after 48 hours — sign up to keep them. ```json { "mcpServers": { "vitrine": { "command": "npx", "args": ["@vitrine3d/mcp"] } } } ``` ## Available Tools [#available-tools] ### Models [#models] | Tool | Description | | ---------------------- | --------------------------------------------------- | | `vitrine_list_models` | List all models with IDs, names, and publish status | | `vitrine_upload_model` | Upload a GLB file from disk | | `vitrine_model_info` | Get model details, thumbnail, and merged config | | `vitrine_delete_model` | Delete a model and its storage | ### Configuration [#configuration] | Tool | Description | | ----------------------- | ------------------------------------------------------------ | | `vitrine_get_config` | Get the full scene config for a model | | `vitrine_set_config` | Apply a config patch (lighting, camera, background, effects) | | `vitrine_list_hdris` | List available HDRI environment presets | | `vitrine_config_schema` | Get the full config JSON schema | ### Showcases & Embeds [#showcases--embeds] | Tool | Description | | ------------------- | ----------------------------- | | `vitrine_publish` | Publish or unpublish a model | | `vitrine_get_embed` | Get ready-to-paste embed HTML | ### Looks [#looks] | Tool | Description | | --------------------- | ----------------------------- | | `vitrine_list_looks` | List your saved Looks | | `vitrine_apply_look` | Apply a Look to a model | | `vitrine_create_look` | Save a config as a named Look | ### Feedback [#feedback] | Tool | Description | | ------------------ | -------------------------------------- | | `vitrine_feedback` | Submit a bug report or feature request | ### Account [#account] | Tool | Description | | ---------------------- | ------------------------------- | | `vitrine_account_info` | Current plan, usage, and limits | ## Resources [#resources] The server exposes read-only resources that agents can reference for context: | Resource | Description | | -------------------------- | ---------------------------- | | `vitrine://config-schema` | Full SceneConfig JSON schema | | `vitrine://hdri-presets` | Available HDRI presets | | `vitrine://embed-template` | Embed code template | ## How It Works [#how-it-works] ``` You → AI Agent (Claude, Cursor, etc.) ↓ MCP protocol vitrine MCP Server (runs locally) ↓ HTTPS api.vitrine3d.com ↓ Supabase (database, storage, auth) ``` The MCP server runs on your machine and translates tool calls into [vitrine API](/docs/api) requests. All data stays in your vitrine account. ## Compatibility [#compatibility] Works with any MCP-compatible client: * Claude Code / Claude Desktop * Cursor * Windsurf * Cline * Any tool supporting the [Model Context Protocol](https://modelcontextprotocol.io) ## Environment Variables [#environment-variables] | Variable | Description | | ----------------- | ------------------------------------------- | | `VITRINE_API_KEY` | Your API key (starts with `vt_`) | | `VITRINE_API_URL` | Override the API base URL (for development) | # Pricing (/docs/pricing) ## Plans [#plans] | | Free | Pro | | ----------------------- | ----------- | ----------------------- | | Published showcases | 5 | Unlimited | | Uploads and drafts | Unlimited | Unlimited | | Editor and settings | Full access | Full access | | Embed code | Yes | Yes | | Thumbnails | Yes | Yes | | Custom Looks & Layouts | Yes | Yes | | Shopify integration | Yes | Yes | | vitrine badge on embeds | Yes | No | | **Price** | **$0** | **$12/mo** (or $120/yr) | ## What counts as "published"? [#what-counts-as-published] A showcase is **published** when you toggle it on in the editor. Only published showcases are accessible via embed code. Unpublished (draft) showcases can be edited and previewed but won't load on your site. The free tier limits the number of published showcases to 5. You can create unlimited drafts and switch which ones are published at any time. ## What's the badge? [#whats-the-badge] Free-tier embeds show a small "Powered by vitrine" badge in the corner of the viewer. Upgrading to Pro removes it. ## Upgrading [#upgrading] 1. Open the [dashboard](https://app.vitrine3d.com) 2. Go to **Account** 3. Click **Upgrade to Pro** 4. Complete payment via Stripe Your plan activates immediately. All existing embeds lose the badge, and the publish limit is lifted. ## FAQ [#faq] ### Can I try before I buy? [#can-i-try-before-i-buy] Yes. The free tier gives you full access to the editor, all effects, and 5 published showcases. No credit card required. You can even try the editor without creating an account — anonymous uploads work for 48 hours. ### Can I downgrade? [#can-i-downgrade] Yes. If you cancel Pro, your plan remains active until the end of the billing period. After that, you return to the free tier. If you have more than 5 published showcases, you'll need to unpublish some to stay within the limit. ### Do you offer annual billing? [#do-you-offer-annual-billing] Yes. Annual billing is $120/year (2 months free compared to monthly). ### Is there a team or agency plan? [#is-there-a-team-or-agency-plan] Not yet. If you need multi-user access or agency pricing, reach out at [support@vitrine3d.com](mailto:support@vitrine3d.com). # Shopify 3D Product Viewer (/docs/shopify) vitrine provides a Shopify app with a theme extension that drops interactive 3D viewers onto your product pages. Set a Showcase ID per product, and the viewer replaces or sits alongside the default product media — no code changes to your theme. ## Get started [#get-started] 1. Install the **vitrine** app from the [Shopify App Store](https://apps.shopify.com/vitrine) 2. In the Shopify theme editor, enable the **vitrine 3D Viewer** extension 3. Set a **Showcase ID** per product (see below) Don't have a Showcase ID yet? Go to [app.vitrine3d.com](https://app.vitrine3d.com), upload a GLB model, style the scene, and publish it. The ID appears in the embed panel. ## Setting the Showcase ID per product [#setting-the-showcase-id-per-product] Each product that should display a 3D viewer needs a Showcase ID stored in a product metafield. There are two ways to set it: ### From the product admin [#from-the-product-admin] 1. Open a product in your Shopify admin 2. Find the **3D Model Settings** block in the product details 3. Paste the Showcase ID and save The app stores the ID as a product metafield automatically. ### From the metafields section [#from-the-metafields-section] You can also set the value directly in the product's **Metafields** section — look for the **Showcase ID** field under the vitrine app namespace. ## Two ways to display the viewer [#two-ways-to-display-the-viewer] The theme extension offers two modes. Pick the one that fits your store layout. ### Media replacement (app embed) [#media-replacement-app-embed] Replaces the native product media gallery with the 3D viewer. Enable the **vitrine 3D Viewer** app embed in your theme settings. It reads the Showcase ID from the product metafield and swaps the entire media section. Works out of the box with Dawn, Horizon, Debut, and most popular themes. If your theme uses a non-standard media container, set a custom CSS selector in the embed settings. You can also set a **Default Showcase ID** as a fallback for products that don't have a per-product metafield. ### Section block [#section-block] Add the **vitrine 3D Viewer** block to any section in the theme editor. This mode places the viewer alongside your existing content rather than replacing media. In the block settings, you can either paste a Showcase ID directly or use **Connect dynamic source** to pull it from the product metafield. This way, the same block shows a different model on each product page. ## Theme compatibility [#theme-compatibility] The extension works with any Online Store 2.0 theme that supports app blocks and embeds. It loads the same `vitrine.js` embed script used by standalone embeds — no iframes, no theme code changes. Auto-detected media containers: * `.product__media-wrapper` (Dawn and most themes) * `.product-information__media` (Horizon) * `.product__media`, `.product-single__media-wrapper` (Debut / older themes) * `.product-gallery`, `[data-product-media-container]` ## Variant switching [#variant-switching] If your product has color or material variants with different 3D models, use the JavaScript API to swap the viewer when the customer picks a variant: ```js document.querySelectorAll('[name="id"]').forEach(el => { el.addEventListener('change', () => { const viewer = document.querySelector('[data-vitrine]'); const showcaseId = getShowcaseIdForVariant(el.value); if (viewer && showcaseId) { viewer.dataset.vitrineId = showcaseId; Vitrine.boot(viewer); } }); }); ``` ## Requirements [#requirements] * Shopify store on any paid plan * A vitrine account (free tier works for up to 5 published showcases) * 3D models in GLB format, uploaded and published via [app.vitrine3d.com](https://app.vitrine3d.com) ## Next steps [#next-steps] * [Getting Started](/docs/getting-started) — upload your first model * [Embed Reference](/docs/embed) — all attributes and the JavaScript API * [Looks & Layouts](/docs/looks-and-layouts) — reuse styles across multiple products # Troubleshooting (/docs/troubleshooting) ## Model doesn't appear [#model-doesnt-appear] **Check the console.** Open browser DevTools (F12) and look for `[vitrine]` messages. Common causes: * **Missing `data-vitrine` attribute** — the `
` must have `data-vitrine` to be recognized * **No dimensions** — the container has zero width or height. Set explicit `width`, `height`, or `aspect-ratio` in CSS * **CORS error** — if using `data-model` with a direct URL, the server must send `Access-Control-Allow-Origin` headers. Showcase IDs (`data-vitrine-id`) don't have this issue * **Network error** — check that the model URL is accessible and returns a valid GLB file ## Model looks dark [#model-looks-dark] The viewer uses environment-based lighting (HDRI). If your model appears dark: * **Check the HDRI** — some presets are intentionally dim. Try switching to a brighter environment in the editor * **Check material properties** — if your model's metallic value is high but roughness is low, it acts as a mirror. Make sure the environment provides something to reflect * **Add direct lights** — enable key/fill/rim lights in the editor for more control ## Model is oriented wrong [#model-is-oriented-wrong] vitrine handles axis conventions automatically (Y-up, Z-up). If the model still appears rotated: * Open the model in the [editor](https://app.vitrine3d.com) * Adjust the orientation mapping in the settings * Re-export from your 3D software with Y-up if possible ## Viewer loads slowly [#viewer-loads-slowly] * **Check file size** — models over 30 MB will load slowly on mobile. See [Supported Formats](/docs/formats) for optimization tips * **Enable thumbnails** — server-rendered poster images give instant visual feedback while the viewer loads * **Check your connection** — the WASM runtime is about 5 MB (compressed). First load downloads it; subsequent loads use the browser cache ## WebGPU not working [#webgpu-not-working] See the dedicated [WebGPU Support](/docs/webgpu) page for browser compatibility and how to enable WebGPU. The viewer falls back to WebGL2 automatically. A small info icon appears in the corner when running in fallback mode. ## Viewer uses too much battery [#viewer-uses-too-much-battery] vitrine uses reactive rendering — it only draws frames when something changes (camera movement, animations). When idle, GPU usage drops to zero. If you notice high GPU usage: * **Auto-rotate** continuously requests frames. Disable it if battery life matters * **Multiple viewers** on one page multiply GPU load. Consider using thumbnail posters for off-screen viewers ## Embed doesn't match editor preview [#embed-doesnt-match-editor-preview] The editor and embed use the same renderer, so they should match exactly. If they differ: * **Check `data-config` overrides** — inline config patches override the saved settings * **Check the Look** — if you changed the Look after generating the embed code, the embed uses the updated Look * **Aspect ratio** — the camera framing adapts to the container's aspect ratio. A different aspect ratio in the embed vs. editor will frame the model differently ## Still stuck? [#still-stuck] Reach out at [support@vitrine3d.com](mailto:support@vitrine3d.com) with: * Your showcase ID or model URL * Browser and OS version * A screenshot or console log of the issue # WebGPU Support (/docs/webgpu) vitrine automatically detects whether your browser supports **native WebGPU**. If it does, the viewer uses WebGPU for better lighting, effects, and rendering quality. If not, it falls back to **WebGL2**, which is supported everywhere and still looks great. The viewer works well on both backends. WebGL2 is not a degraded experience — it just doesn't have access to some advanced rendering features that WebGPU enables. ## WebGPU-only features [#webgpu-only-features] These features require WebGPU and are silently skipped on WebGL2: * **Depth of field** (Gaussian bokeh) * **Multiple direct lights with shadows** (WebGL2 supports one) ## Why did I get a fallback message? [#why-did-i-get-a-fallback-message] If you see a `[vitrine] Using WebGL2` message in the browser console, one of these was detected: | Reason | What it means | | --------------------------- | ---------------------------------------------------------------------------------- | | Software-only GPU | No hardware GPU detected. The browser would render in software, which is too slow. | | ANGLE / OpenGL ES emulation | WebGPU is routed through a compatibility layer instead of a native graphics API. | | Unknown architecture | The browser couldn't identify the GPU well enough to guarantee WebGPU works. | | WebGPU not available | The browser doesn't support WebGPU at all (older Firefox, Safari without flags). | ## How to enable native WebGPU [#how-to-enable-native-webgpu] ### Chrome / Edge [#chrome--edge] * **Update your browser** to the latest version. WebGPU is enabled by default in Chrome 113+. * **Update your GPU drivers** — outdated drivers can cause Chrome to fall back to ANGLE/OpenGL ES. * **Linux: enable Vulkan** — go to `chrome://flags/#enable-vulkan` and set it to *Enabled*. Without Vulkan, Chrome routes WebGPU through ANGLE. * Check `chrome://gpu` — look for *"WebGPU: Hardware accelerated"* and confirm the Dawn adapter isn't using an OpenGL ES backend. ### Firefox [#firefox] * WebGPU is behind a flag: go to `about:config` and set `dom.webgpu.enabled` to `true`. ### Safari [#safari] * WebGPU is available in Safari 18+. Enable it in *Settings > Feature Flags > WebGPU* if it isn't on by default. ## For merchants [#for-merchants] Your customers automatically get the best backend their browser supports — no configuration needed. The fallback is seamless: same model, same configuration, same embed code. To verify what backend a specific visitor is using, open the browser console (F12 → Console tab) and look for the `[vitrine]` log line. # WordPress 3D Product Viewer (/docs/wordpress) vitrine provides a WordPress plugin with a native Gutenberg block. Search "vitrine" in the block inserter, paste your Showcase ID, and the 3D viewer appears on the published page — with a thumbnail preview right in the editor. The vitrine block in the WordPress editor with thumbnail preview and settings sidebar ## Get started [#get-started] 1. [Install the plugin](https://wordpress.org/plugins/vitrine-3d-viewer/) from the WordPress plugin directory 2. In any page or post, add the **vitrine 3D Viewer** block 3. Paste your **Showcase ID** in the sidebar and pick an aspect ratio Don't have a Showcase ID yet? Go to [app.vitrine3d.com](https://app.vitrine3d.com), upload a GLB model, style the scene, and publish it. The ID appears in the embed panel. ## WooCommerce product pages [#woocommerce-product-pages] Add the vitrine block to any WooCommerce product description. The block works in both the product block editor and classic editor (via the block inserter). For store-wide 3D on every product, add the block to a WooCommerce product template in the Site Editor (FSE). ### Variant switching [#variant-switching] If your product has color or material variants with different 3D models, use the JavaScript API to swap the viewer when the customer picks a variant: ```js jQuery('.variations_form').on('found_variation', function(event, variation) { const el = document.querySelector('[data-vitrine]'); const showcaseId = variation.vitrine_id; // from a custom field if (el && showcaseId) { el.dataset.vitrineId = showcaseId; Vitrine.boot(el); } }); ``` ## Next steps [#next-steps] * [Getting Started](/docs/getting-started) — upload your first model * [Embed Reference](/docs/embed) — all attributes and the JavaScript API * [Supported Formats](/docs/formats) — GLB requirements and optimization tips