Building a Product Customiser¶
Hey there! 
In this tutorial we'll build a simple product configurator — let users pick colours, add text, and switch between material variants. It's the kind of thing you'd see in a sneaker builder or merch customiser.
We'll be using the Customiser Plugin, which adds a handful of powerful methods to the viewer.
This tutorial builds off the Basic implementation tutorial.
Let's build!
1. Load the Plugin¶
Start with the basic viewer boilerplate, but this time we also load the Customiser plugin script and tell the viewer to use it via data-270-plugins:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Product Customiser</title>
<style>
body {
font-family: sans-serif;
}
main {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
#viewer-container {
width: 500px;
height: 500px;
}
.customiser {
max-width: 500px;
margin-top: 1rem;
display: flex;
flex-direction: column;
gap: .75rem;
}
.swatches {
display: flex;
gap: .5rem;
}
.swatches button {
width: 36px;
height: 36px;
border: 2px solid #ccc;
border-radius: 4px;
cursor: pointer;
}
.customiser label {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
.customiser input[type="text"] {
padding: .3rem .5rem;
font-size: .9rem;
}
</style>
</head>
<body>
<h1>Product Customiser</h1>
<main>
<div id="viewer-container">
<div class="js-270viewer"
data-270-model="lunchbox"
data-270-background="#eeeeee"
data-270-plugins="customiser">
</div>
</div>
<div class="customiser">
<div>
<strong>Colour</strong>
<div class="swatches">
<button style="background:#e74c3c" data-color="#e74c3c"></button>
<button style="background:#3498db" data-color="#3498db"></button>
<button style="background:#2ecc71" data-color="#2ecc71"></button>
<button style="background:#f1c40f" data-color="#f1c40f"></button>
<button style="background:#1a1a1a" data-color="#1a1a1a"></button>
<button style="background:#ffffff" data-color="#ffffff"></button>
</div>
</div>
<label>Text
<input type="text" id="custom-text" placeholder="Your name here" maxlength="20" />
</label>
</div>
</main>
<script>window.TSDAPIKEY = 'rest-of-the-owl';</script>
<script src="https://api.270degrees.nl/api/script/latest/viewer.js"></script>
<script src="https://api.270degrees.nl/api/script/latest/customiser.js"></script>
<script>
// We'll fill this in next
</script>
</body>
</html>
Notice:
- We load
customiser.jsafterviewer.js - We pass
data-270-plugins="customiser"on the viewer element to attach it automatically
Use the el.getObjectNames() method to see the available objects to pass in your methods.
We'll use the objects trommel and text in this example.
Important: Wait for the Model to Load¶
Before we start changing colours or adding text, the model and plugin need to be fully loaded. We'll use onLoadComplete via TSDViewer.create() to make sure everything is ready:
<script>
const $viewer = document.querySelector('.js-270viewer');
TSDViewer.create($viewer, {
onLoadComplete: () => {
// The model and Customiser plugin are ready.
// We'll add our controls here.
}
});
</script>
Everything from here on goes inside that onLoadComplete callback.
2. Add Colour Swatches¶
Now let's wire up those colour buttons using setColor:
const targetObject = 'trommel';
document.querySelectorAll('.swatches button').forEach(btn => {
btn.addEventListener('click', () => {
$viewer.setColor({
name: targetObject,
color: btn.dataset.color
});
});
});
Click a swatch and the object changes colour on the spot. The method accepts any valid CSS colour — hex, rgb(), or named colours like red.
3. Add Custom Text¶
Let's let users stamp their name on the product using setText:
document.querySelector('#custom-text').addEventListener('input', (ev) => {
$viewer.setText({
name: 'text',
text: ev.target.value,
color: 'grey',
size: 72
});
});
Type in the text field and watch it appear on the model in real time.
You can tweak font, fontStyle, fontWeight, and size — check the full setText reference for all options.
If you want to clear the text, use removeText: $viewer.removeText({ name: targetObject }).
4. End Result¶
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Product Customiser</title>
<style>
body {
font-family: sans-serif;
}
main {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
#viewer-container {
width: 500px;
height: 500px;
}
.customiser {
max-width: 500px;
margin-top: 1rem;
display: flex;
flex-direction: column;
gap: .75rem;
}
.swatches {
display: flex;
gap: .5rem;
}
.swatches button {
width: 36px;
height: 36px;
border: 2px solid #ccc;
border-radius: 4px;
cursor: pointer;
}
.customiser label {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
.customiser input[type="text"] {
padding: .3rem .5rem;
font-size: .9rem;
}
</style>
</head>
<body>
<h1>Product Customiser</h1>
<main>
<div id="viewer-container">
<div class="js-270viewer"
data-270-model="lunchbox"
data-270-background="#eeeeee"
data-270-plugins="customiser">
</div>
</div>
<div class="customiser">
<div>
<strong>Colour</strong>
<div class="swatches">
<button style="background:#e74c3c" data-color="#e74c3c"></button>
<button style="background:#3498db" data-color="#3498db"></button>
<button style="background:#2ecc71" data-color="#2ecc71"></button>
<button style="background:#f1c40f" data-color="#f1c40f"></button>
<button style="background:#1a1a1a" data-color="#1a1a1a"></button>
<button style="background:#ffffff" data-color="#ffffff"></button>
</div>
</div>
<label>Text
<input type="text" id="custom-text" placeholder="Your name here" maxlength="20" />
</label>
</div>
</main>
<script>window.TSDAPIKEY = 'rest-of-the-owl';</script>
<script src="https://api.270degrees.nl/api/script/latest/viewer.js"></script>
<script src="https://api.270degrees.nl/api/script/latest/customiser.js"></script>
<script>
const $viewer = document.querySelector('.js-270viewer');
TSDViewer.create($viewer, {
onLoadComplete: () => {
const targetObject = 'trommel';
// Colour swatches
document.querySelectorAll('.swatches button').forEach(btn => {
btn.addEventListener('click', () => {
$viewer.setColor({
name: targetObject,
color: btn.dataset.color
});
});
});
// Custom text
document.querySelector('#custom-text').addEventListener('input', (ev) => {
$viewer.setText({
name: 'text',
text: ev.target.value,
color: 'grey',
size: 72
});
});
}
});
</script>
</body>
</html>
What's Next?¶
You've got a working product customiser! Users can pick colours and add their own text right on the 3D model.
A few ideas to take it further:
- Apply images with
setTexturefor logos or patterns - Switch between material presets with
setVariant - Combine with the Photo Studio to let users capture their custom creation
- Read the full Customiser Plugin docs for all options