Skip to main content

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
}
PropertyTypeDescription
idstringUnique model identifier.
namestringDisplay name.
print_area{ width, height }Texture resolution in pixels. The canvas printingBox aspect ratio should match to avoid distortion.
camera_znumberInitial camera distance from the model.
base_material_metalnessnumberPBR metalness for the base material (0–1).
base_material_roughnessnumberPBR roughness for the base material (0–1).
material_colornumberBase material color (hex integer, e.g. 16777215 for white).
ambient_Light_colornumberAmbient light color.
print_paddingnumberPadding around the print area texture.
only_exportablebooleanIf true, only used for export (not interactive preview).
customMaterialLayersRecord<string, string>Map of mesh name to normal map filename.

Mesh Naming Convention

Name your FBX meshes to map them to canvas views:

Mesh NameMaps To
customView 0 (first view)
custom_1 or custom1View 1
custom_2 or custom2View 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.

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.

PropertyTypeDefaultDescription
placement'designer' | 'panel' | 'external''external'Where the 3D preview is rendered.
modelsPathstring'./models/'Path to the models directory.
autoRotatebooleanfalseAuto-rotate the model.
autoRotateSpeednumber2.0Auto-rotation speed.
backgroundColorstring'transparent'Renderer background color.
cameraFovnumber45Camera field of view in degrees.
enableControlsbooleantrueEnable orbit controls (drag to rotate, scroll to zoom).
enableDampingbooleantrueSmooth damping for orbit controls.
textureDebounceMsnumber300Debounce 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