Skip to content

Colors & tint

How an item gets its colour. There are two layers, and they stack:

  1. The baked texture that lives inside the .glb — this is what renders. ✅ Available
  2. An optional per-channel tint (from the item JSON) that multiplies the texture, so you can recolour specific zones without re-exporting the mesh. It applies from your tint_channels defaults; there is no in-game player recolour yet. ⚠️ Partial

Bake the item’s texture into the .glb when you export it from Blender (built-in File → Export → glTF 2.0 (.glb), “Include → Images: Automatic”). There is no separate texture file and no models/item PNG for the worn mesh — the renderer reads the texture straight out of the GLB.

A textured GLB now renders that texture; a tinted one renders texture × tint.

If every part of the item is one fixed look, you’re done after baking the texture. Just omit tint_channels — with no channels declared, the mesh renders exactly as authored. (supports_color is irrelevant here either way — see the caution below.)

{
"type": "tiedup:bondage_item",
"display_name": "Ropes",
"model": "tiedup:models/gltf/binds/ropes.glb",
"regions": ["ARMS"]
}

Tint lets you recolour named zones at render time without touching the mesh — a ball gag’s leather strap can stay black while the ball is tinted red. The colour is the per-channel default you declare in the item JSON (one colour per channel, the same for every stack of that item — there is no per-stack player recolour today). It works by multiplying the baked texture by that colour: finalPixel = texture × tint.

1 — Name the materials

In Blender, give each recolourable zone a material whose name starts with tintable_ (e.g. tintable_1, tintable_2). Any other material name renders fixed.

2 — Author grayscale

Paint the tintable zones in grayscale. The tint colour replaces the hue, so a grayscale base reads correctly as texture × tint. A coloured base would multiply twice and look muddy.

The material name in the GLB is the channel name, and it must match the key you use in the item JSON.

Material nameBehaviour
tintable_1, tintable_2, …Tintable zone — one independent recolour channel per name
anything else (strap, buckle, metal)Fixed, renders as baked

Numbers don’t have to be sequential — tintable_1 and tintable_5 is fine; the number is just part of the name. Example for a ball gag:

MaterialTextureBehaviour
strapleather colourfixed
tintable_1grayscale ball surfacerecolourable
bucklechromefixed

Use luminance-only (grayscale) textures for tintable zones. A mid-gray base (~128) gives more natural results than pure white: dark pixels stay dark whatever the tint, which preserves shadows and crevices.

Gray inputTintResult
White (255)Red (255, 0, 0)bright red
Mid-gray (128)Red (255, 0, 0)dark red
Black (0)Red (255, 0, 0)black (shadow preserved)

Declare each channel’s colour under tint_channels. Keys match the tintable_ material names from the GLB; values are hex, with or without a leading #. tint_channels is the only field that drives bondage tinting — the render path reads it directly.

{
"type": "tiedup:bondage_item",
"display_name": "Ball Gag",
"model": "tiedup:models/gltf/gags/ball_gag.glb",
"regions": ["MOUTH"],
"tint_channels": {
"tintable_1": "#FF0000"
}
}

The values in tint_channels are read per item at render time. There is no player recolour UI today — the tint_channels defaults you declare are what you always see. ❌ Planned

  1. Split the mesh into fixed materials and tintable_N materials in Blender.
  2. Paint the tintable zones in grayscale (mid-gray base).
  3. Bake & export the texture into the .glb (built-in glTF 2.0 exporter).
  4. Declare the tint_channels defaults in the item JSON, with each key matching a tintable_ material name. (Don’t bother with supports_color — it’s inert on the bondage path.)
  5. /reload and check the item in-game — confirm the texture shows and each channel tints the right zone.