Quick Start
This guide creates the complete CustomForge Workbench with the built-in demonstration product, so no model or texture files are required yet
1 Create a mount element
<div id="app"></div>The Workbench must have a measurable height
html,
body,
#app {
height: 100%;
margin: 0;
}2 Create the Workbench
import { createWorkbench } from 'customforge/workbench'
import 'customforge/style.css'
const workbench = await createWorkbench({
container: '#app',
})
workbench.customizer.addText({
text: 'CustomForge',
x: 690,
y: 320,
width: 220,
color: '#182023',
})
workbench.customizer.clearSelection()createWorkbench() resolves after the editor, viewer, product, and live texture bridge are ready
Omitting fontSize uses the 22px default
Because no product was provided, this example uses the bundled cup_decal_small_margins.glb cup and its PrintArea Mesh
3 Try the editor
The initial Workbench provides
- A 2D texture editor on the left
- A layer panel for object ordering and state
- A live 3D product preview on the right
- Text and image dialogs
- An Office-style contextual formatting bar for selected text
- Undo, redo, Design JSON, product loading, PNG export, and camera controls
Drag, resize, or rotate the text on the 2D canvas and the 3D preview updates immediately
4 Load your own product
Once you have a UV-mapped model, pass its remote URL during initialization
const workbench = await createWorkbench({
container: '#app',
product: {
modelUrl: 'https://cdn.example.com/products/mug.glb',
textureUrl: 'https://cdn.example.com/products/mug-base.png',
surfaceMesh: 'PrintArea',
textureFlipY: false,
},
})textureUrl is optional, while modelUrl must point to a model with valid UV coordinates
See Product Assets before exporting a model for CustomForge
5 Release resources
Each instance owns DOM listeners, Fabric state, WebGL resources, and observers
Destroy it when the page or host component is no longer in use
window.addEventListener('beforeunload', () => workbench.destroy(), {
once: true,
})In Vue, React, or another component framework, call destroy() from the component unmount lifecycle instead
