3D Preview
The cv-3d-preview addon renders a Three.js 3D model with the user's canvas design applied as a real-time texture. Canvas changes are debounced and baked onto the model's printable mesh surfaces.
Prerequisites
Install three as a peer dependency:
pnpm add three
pnpm add -D @types/three
Three.js is dynamically imported — it stays out of the main bundle until the 3D preview is used.
Model Directory Structure
Each 3D model lives in its own directory under the configured modelsPath:
models/
└── cup/
├── config.json # Model configuration
├── model.fbx # 3D model (FBX format)
└── environment_map.hdr # HDR environment map for lighting
config.json
{
"id": "cup",
"name": "Cup",
"print_area": { "width": 2048, "height": 3072 },
"camera_z": 300,
"base_material_metalness": 0,
"base_material_roughness": 0.5
}
| Property | Type | Description |
|---|---|---|
id | string | Unique model identifier. |
name | string | Display name. |
print_area | { width, height } | Texture resolution in pixels. The canvas printingBox aspect ratio should match to avoid distortion. |
camera_z | number | Initial camera distance from the model. |
base_material_metalness | number | PBR metalness for the base material (0–1). |
base_material_roughness | number | PBR roughness for the base material (0–1). |
material_color | number | Base material color (hex integer, e.g. 16777215 for white). |
ambient_Light_color | number | Ambient light color. |
print_padding | number | Padding around the print area texture. |
only_exportable | boolean | If true, only used for export (not interactive preview). |
customMaterialLayers | Record<string, string> | Map of mesh name to normal map filename. |
Mesh Naming Convention
Name your FBX meshes to map them to canvas views:
| Mesh Name | Maps To |
|---|---|
custom | View 0 (first view) |
custom_1 or custom1 | View 1 |
custom_2 or custom2 | View 2 |
| ... | ... |
Connecting a Model to a View
Set threeJsPreviewModel in the view's options to the model directory name:
const product = {
title: 'Custom Cup',
views: [
{
title: 'Front',
options: {
stageWidth: 700,
stageHeight: 1000,
threeJsPreviewModel: 'cup',
},
elements: [...],
printAreas: [
{
printingBox: { left: 40, top: 40, width: 615, height: 921 },
},
],
},
{
title: 'Back',
options: {
stageWidth: 700,
stageHeight: 1000,
},
elements: [...],
printAreas: [
{
printingBox: { left: 40, top: 40, width: 615, height: 921 },
},
],
},
],
};
Only one view needs threeJsPreviewModel — the addon uses this to determine which model to load. All views with print areas will have their textures baked onto the corresponding custom / custom_1 meshes.
Print Area Aspect Ratio
The printingBox aspect ratio should match the model's print_area ratio. If they differ by more than 2%, a console warning is emitted:
[cv-3d-preview] Print area aspect ratio mismatch:
model expects 2048×3072 (0.667), canvas print area is 615×921 (0.668).
Adjust the view's printingBox to match the model's print_area ratio for undistorted textures.
Placement Modes
Configure where the 3D preview appears via modulesConfig.threeDPreview.placement:
'designer' — Mini Overlay
A small preview overlaid on the bottom-left corner of the canvas area. Users can expand it to full canvas size with the toggle button.
createCustomizer('#container', {
modulesConfig: {
threeDPreview: {
placement: 'designer',
modelsPath: './models/',
},
},
});
'panel' — Side-by-Side
The 3D preview takes equal space next to the canvas inside cv-customizer.
createCustomizer('#container', {
modulesConfig: {
threeDPreview: {
placement: 'panel',
modelsPath: './models/',
},
},
});
'external' — Standalone (default)
Place the <cv-3d-preview> element anywhere on your page and pass the chamevo instance:
<cv-customizer id="cust"></cv-customizer>
<cv-3d-preview id="preview" style="height: 500px; display: block"></cv-3d-preview>
<script type="module">
import { createCustomizer } from '@chamevo/customizer';
const cust = createCustomizer('#cust', {
modulesConfig: {
threeDPreview: { modelsPath: './models/' },
},
});
cust.addEventListener('cvReady', (e) => {
document.getElementById('preview').chamevo = e.detail.chamevo;
});
</script>
Configuration Reference (CV3DPreviewConfig)
Set via modulesConfig.threeDPreview or the component's config prop.
| Property | Type | Default | Description |
|---|---|---|---|
placement | 'designer' | 'panel' | 'external' | 'external' | Where the 3D preview is rendered. |
modelsPath | string | './models/' | Path to the models directory. |
autoRotate | boolean | false | Auto-rotate the model. |
autoRotateSpeed | number | 2.0 | Auto-rotation speed. |
backgroundColor | string | 'transparent' | Renderer background color. |
cameraFov | number | 45 | Camera field of view in degrees. |
enableControls | boolean | true | Enable orbit controls (drag to rotate, scroll to zoom). |
enableDamping | boolean | true | Smooth damping for orbit controls. |
textureDebounceMs | number | 300 | Debounce delay (ms) before re-baking textures after canvas changes. |
Color-Linked Layers
Elements with a colorLink3DLayer property sync their fill color to a named mesh in the 3D model. This is useful for product parts like body color or lid color that should update in real time:
{
title: 'Cup Body Color',
type: 'image',
source: '/images/cup-body.svg',
parameters: {
colorLink3DLayer: 'body',
colors: ['#ffffff', '#000000', '#ff0000'],
// ...
},
}
When the user changes this element's color, the body mesh in the 3D model updates to match.
Next Steps
- Print Areas — Configure print areas that map to 3D model textures
- Module Configs —
modulesConfig.threeDPreviewoptions - Products & Views — View
options.threeJsPreviewModel