Materials
Manage PBR materials with texture maps. Create realistic surface properties for your 3D models.
baseColor, metallic, roughness, opacity (0–100), specular, reflectivity, reflectionStrength, transparencyMode ("Opaque" or "Transparent"), ior, emissive, doubleSided, the texture maps and the gemstone fields below. A nested pbr object is stored as sent but the viewer does not read it.List Materials
const response = await fetch(BASE_URL + '/materials', {
headers: { 'Authorization': 'Bearer ' + accessToken }
});
const data = await response.json();
// { "success": true, "materials": [{ "id": "mat_123", "name": "Brushed Metal",
// "baseColor": "#808080", "metallic": 0.9, "roughness": 0.3, ... }] }Create Material
const response = await fetch(BASE_URL + '/materials', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Oak Wood',
category: 'wood',
baseColor: '#8B6F47',
metallic: 0.0,
roughness: 0.8
})
});Update Material
const response = await fetch(BASE_URL + '/materials/mat_123', {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({ roughness: 0.5, metallic: 0.8 })
});Delete Material
const response = await fetch(BASE_URL + '/materials/mat_123', {
method: 'DELETE',
headers: { 'Authorization': 'Bearer ' + accessToken }
});Gemstone & glass materials
A material can let light pass through it: faceted stones are ray traced inside their mesh (refraction, internal reflections, dispersion, body colour) and glass refracts through thin walls. All eight fields are optional — a material without them renders exactly as before.
| Field | Value | Meaning |
|---|---|---|
transmission | 0–1 | How much light passes through. 0 or absent = an ordinary material. |
transmissionMode | "gemstone" | "glass" | Gemstone ray traces a closed faceted mesh; glass refracts thin-walled objects (bottles, lenses). |
ior | 1–2.5 | How strongly light bends (water 1.33, glass 1.5, sapphire 1.77, diamond 2.42). |
dispersion | 0–1 | Colour fringing (“fire”), 20 / Abbe number (diamond 0.36, cubic zirconia 0.59). |
attenuationColor | #RGB or #RRGGBB | Body colour: the colour light keeps after attenuationDistance. |
attenuationDistance | 0–100 | Multiple of the object’s size. Smaller = deeper colour; 0 = colourless. |
thickness | 0–4 | Glass wall thickness, as a multiple of the object’s size (e.g. 0.05). |
envMapIntensity | 0–5 | Environment reflection strength of a transmissive material (sparkle). |
gemPreset | preset id | A label for the stone (the table below). The API stores the fields you send; it does not fill them from the preset. |
Keep a fallback look. Devices without gemstone rendering, AR and older embedded viewers show the material’s ordinary look instead. Give a transmissive material transparencyMode: "Transparent" and an opacity below 100 so that look is see-through, not solid. validate_project (MCP) reports materials whose fallback is opaque.
const response = await fetch(BASE_URL + '/materials', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Blue sapphire',
gemPreset: 'Sapphire',
transmission: 1,
transmissionMode: 'gemstone',
ior: 1.77,
dispersion: 0.26,
attenuationColor: '#2350e0',
attenuationDistance: 1.1,
envMapIntensity: 1.15,
// fallback look
baseColor: '#1f4fd6',
transparencyMode: 'Transparent',
opacity: 75,
roughness: 0,
specular: 1
})
});On PUT /materials/:id, null removes a gemstone field and transmission: 0 turns light transmission off. An invalid value is refused with 400, the field name and a stable code:
// 400
{ "success": false, "error": "transmission must be a number from 0 to 1.",
"field": "transmission", "code": "invalid_material_field" }For a material that transmits light, ior must be 1–2.5 whenever the request sets ior or transmission. Other materials accept any IOR, as before.
Preset values
| gemPreset | ior | dispersion | attenuationColor | attenuationDistance | Fallback colour / opacity |
|---|---|---|---|---|---|
Diamond Diamond | 2.417 | 0.36 | #ffffff | 0 | #ffffff / 35 |
Sapphire Blue sapphire | 1.77 | 0.26 | #2350e0 | 1.1 | #1f4fd6 / 75 |
Ruby Ruby | 1.77 | 0.26 | #d1123a | 1.1 | #c8102e / 75 |
Emerald Emerald | 1.58 | 0.27 | #23a35c | 1.3 | #1e9e5a / 75 |
Amethyst Amethyst | 1.55 | 0.27 | #9058d8 | 1.8 | #8e4fd6 / 65 |
CubicZirconia Cubic zirconia | 2.17 | 0.59 | #ffffff | 0 | #ffffff / 35 |
Moissanite Moissanite | 2.5 | 0.75 | #fbfaf2 | 40 | #fbfaf2 / 35 |
Aquamarine Aquamarine | 1.58 | 0.27 | #84d8ea | 3.5 | #7fd3e6 / 55 |
Topaz Blue topaz | 1.62 | 0.26 | #4aade6 | 2.6 | #45a8e0 / 60 |
Morganite Morganite | 1.58 | 0.27 | #f4abb8 | 3.5 | #f2a6b3 / 55 |
Peridot Peridot | 1.67 | 0.33 | #a0cc3a | 1.8 | #9cc73a / 65 |
Citrine Citrine | 1.55 | 0.27 | #f4b43a | 2.2 | #f2b233 / 65 |
Tanzanite Tanzanite | 1.69 | 0.43 | #5e52d4 | 1.2 | #5b4fcf / 70 |
Garnet Garnet | 1.75 | 0.3 | #8f1d2e | 1 | #8b1a2b / 80 |
PinkSapphire Pink sapphire | 1.77 | 0.26 | #ea568e | 1.6 | #e8508a / 70 |
Lighting environments
The lighting a stone reflects is a project setting: projectSettings.environmentSource ("studio" — the default — "jewelry", "softbox", "daylight" or "custom") with environmentRotation in degrees, and projectSettings.gemQuality ("auto", "high", "standard", "fallback").
"custom" lights the project with your own equirectangular HDR panorama. Upload it as a Radiance RGBE .hdr — at most 16 MB and 4096×2048; 2048×1024 is plenty (the editor converts .exr files and resizes larger images before uploading) — then save the returned path as projectSettings.environmentCustomPath with environmentSource: "custom". These endpoints take a session token; an X-API-Key does not reach them.
const form = new FormData();
form.append('file', hdrFile); // Radiance RGBE .hdr
form.append('originalName', 'studio.hdr'); // optional, shown in the editor
const response = await fetch(BASE_URL + '/projects/' + projectId + '/environment', {
method: 'POST',
headers: { 'Authorization': 'Bearer ' + accessToken },
body: form
});
// 200 { "success": true, "environmentId": "…",
// "path": "<owner>/project-environments/<project>/<id>/environment.hdr",
// "fileName": "studio.hdr", "width": 2048, "height": 1024, "bytes": 6291456,
// "url": "<signed URL, valid 6 hours>" }
// 400 { "success": false, "code": "invalid_content", "error": "…" } not RGBE, or over 4096×2048
// 413 { "success": false, "code": "file_too_large", "error": "…" }Returns { "success": true, "url": "…", "expiresIn": 21600 } — a signed URL for a path in the project owner’s environment folder (404 for any other path). The public share payload (GET /share/:projectId/:token) carries project.data.environmentUrl, a one-year signed URL, when the project lights with its own file; a headless viewer loads that URL as an equirectangular RGBE image.
Uploaded files are kept when the setting changes: projects copied from this one as a template may still light with them.