Variants are alternate rendered frame sets for a project. Each variant shares the same camera orbit, frame count, and resolution as the base project — the only difference is the rendered content. Common uses are color options, material swaps, alternate backgrounds, and separately rendered product states.
The API uses the resource name colorway for backward compatibility. The dashboard and public documentation use variant because the feature applies to any kind of alternate render, not just color.
Plan limits
The number of variants you can add per project depends on your plan.
| Plan | Variants per project |
|---|---|
| Hobby | 1 |
| Growth | 3 |
| Pro | 10 |
| Custom | Unlimited |
The Studio interface displays your current count against the limit next to the Colorways header. When you reach the limit, the Create & render button is disabled for new variants. Replacing an existing variant is always allowed and does not count as a new creation.
Base frame set
Every project has one base frame set. This is the first render you uploaded when you created the project. The base frame set is always available in the embed and is the fallback when no variant is explicitly selected.
Additional variants are optional. They appear in the embed variant switcher only after their upload is complete and they transition to the Ready state.
Create a variant in Studio
- Open a project in the dashboard and go to the Studio tab.
- Scroll to the Colorways panel.
- Click Create & render.
- Enter a variant name such as
Walnut,Matte black, orLifestyle photo. - Optionally enable material tint and set color, metalness, and roughness values.
- Choose a background: project default, transparent, solid color, or an uploaded image.
- The renderer re-encodes the base GLB with the new settings and uploads the result.
The variant uses the project's capture settings so each alternate render matches the frame count, row count, resolution, and orbit of the base.
Replace a variant
Select an existing variant in the Colorways list, then click Render selected. The variant keeps its ID, so embeds and JavaScript runtime messages that reference it continue to work after the new frames are ready.
Replace a single frame
To update one frame without re-rendering the full variant, go to the Frames tab, select the variant from the source selector, hover the frame you want to change, and click Replace. Upload a replacement image in the same format as the project's export (WebP, JPEG, or PNG).
On confirmation the frame is uploaded directly to storage. The RenderVersion is bumped automatically so CDN and browser caches serve the updated frame immediately — no manual cache invalidation required.
Variant states
| State | Embed behavior |
|---|---|
| Ready | Variant is selectable and appears in the switcher |
| Pending | Variant is hidden from the switcher until upload completes |
Pending variants are intentionally invisible to the embed so shoppers never see an incomplete frame set.
Embed switcher
When more than one ready variant exists, a compact switcher appears in the embed. The base frame set is labeled Default. Each variant uses its saved name.
Disable the built-in switcher and control variants programmatically with JavaScript SDK when your storefront already has its own product option UI.
JavaScript runtime switching
Switch variants from your storefront JavaScript without reloading the embed.
// Switch to a specific variant by ID
window.postMessage(
{
source: 'meshless-viewer-config',
event: 'setColorway',
data: { colorwayId: 'VARIANT_ID' },
},
'*',
);
// Return to the base frame set (pass empty string or any non-matching ID)
window.postMessage(
{
source: 'meshless-viewer-config',
event: 'setColorway',
data: { colorwayId: '' },
},
'*',
);
setColorway is broadcast to all mounted viewers on the page. If you have multiple viewers, keep each in its own isolated section and send the message from a scoped handler.
Connect your existing color swatch UI to the embed so the viewer updates when the shopper picks a color:
document.querySelectorAll('[data-color-id]').forEach((swatch) => {
swatch.addEventListener('click', () => {
window.postMessage(
{
source: 'meshless-viewer-config',
event: 'setColorway',
data: { colorwayId: swatch.dataset.colorId },
},
'*',
);
});
});
Naming guidance
- Use shopper-facing names —
Walnut,White oak,Graphite. - Avoid internal render codes like
chair_v4_final_APPROVED. - Keep names short enough for the embed switcher (under 20 characters).
- Reuse the same variant ID when replacing frames for an existing option so referencing code does not break.
Relationship to hotspots
Saved hotspots are project-level annotations. They render across all variants when the product geometry and camera orbit are consistent between renders.
If a feature appears only in one variant, use dynamic hotspots from the embed page or JavaScript runtime. Dynamic hotspots are not stored in the project and only appear when your code provides them — you control exactly which variant state shows which callout.
API reference
Use the REST API to create, rename, delete, and generate upload URLs for variants programmatically. The resource is named colorway in all API paths.
GET /api/projects/{projectId}/colorways
POST /api/projects/{projectId}/colorways
PUT /api/projects/{projectId}/colorways/{colorwayId}
DELETE /api/projects/{projectId}/colorways/{colorwayId}
POST /api/projects/{projectId}/colorways/{colorwayId}/generate-upload-urls
POST /api/projects/{projectId}/colorways/{colorwayId}/complete-upload
POST /api/projects/{projectId}/colorways/{colorwayId}/frames/{frameIndex}/generate-replace-url
POST /api/projects/{projectId}/colorways/{colorwayId}/frames/{frameIndex}/confirm-replace
The frame replace endpoints follow the same pattern as the base model replace endpoints. generate-replace-url returns a presigned R2 PUT URL; confirm-replace finalises the upload, adjusts stored byte accounting, and bumps RenderVersion.
See API Reference for request and response shapes.