Colorable Components
A colorable component is a paintable zone on a car: a group of renderers whose color comes from the style system rather than being baked into the model’s textures. One model can carry many paint schemes, because only the color changes between them.
It’s the simplest form of car customization and needs no scripting. Paint Maps build on it to put several colors on a single zone.
Nearly every freight car Railroader ships is built this way, so there are plenty of working examples to read. Each pack’s Definitions.json lives at Railroader_Data/StreamingAssets/AssetPacks/<pack>/ in the game’s install folder. xm-boxcar01 is a minimal example, and ld-sd7 and xm-boxcar05 show the two multi-group patterns described below.
How it works
Section titled “How it works”Three pieces connect a mesh to a color:
- A
RendererGroupcomponent on the car definition names a material on the model and gives it a style class. - A
CarStylesets acolorfor the selectorRenderer.<styleClass>. - When a style is applied, the game resolves that selector and writes the color onto the material.
The group isn’t an explicit list of renderers: every renderer slot pointing at the same source material is rewritten, on load, to one per-car copy of it. Recoloring that instance recolors every mesh using it, across the body and any child models that resolve the same material by name.
Each material takes one color, so two zones that recolor independently need two materials on the model and two RendererGroup components.
Each material is a separate draw call, so we use this technique sparingly on freight cars. On locomotives the extra cost is relatively smaller.
Setting it up
Section titled “Setting it up”Give the model a material intended to be recolored (Railroader’s own cars typically call theirs Colorable), then add a RendererGroup component naming it:
- Kind —
RendererGroup - StyleClass —
body - Material —
MaterialName:Colorable - ColorGamma —
1(optional brightness correction; see below)
In plain terms: every mesh using the Colorable material is painted by whatever the car’s style sets for Renderer.body. You write only the class half; the Renderer. prefix is added for you. The material name must exist on the model, or the component fails to build. See RendererGroup for the full field reference.
Then a CarStyle supplies the color under that selector:
"styles": { "Renderer.body": { "color": "1B3D2F" }}Any style targeting Renderer.body now repaints this car, including one a player picks from the Customize window.
Multiple groups
Section titled “Multiple groups”Cars commonly carry more than one RendererGroup, for two quite different reasons.
Several materials, one color. Give each group the same StyleClass and a different material, and a single style color paints all of them. This is the common case: the SD7 splits its body across four groups (BodyColorable, CabColorable, FrameColorable, MiscColorable), every one on body, so one Renderer.body color paints the whole locomotive. Models get split this way for texturing and authoring reasons, not styling ones, and the style system doesn’t care how many materials are involved.
Separate zones, separate colors. Give each group its own StyleClass to color them independently. xm-boxcar05 does this with body (material Colorable) and roof (material ColorableRoof), letting a paint scheme set the roof apart from the sides:
"styles": { "Renderer": { "color": "2D5A27" }, "Renderer.roof": { "color": "D4AF37" }}The bare Renderer selector sets a default for every group on the car, and Renderer.roof overrides it for the roof. See Specificity.
Independent zones scale to a handful comfortably. It stops scaling when a paint scheme needs many sharply-bounded colors on one continuous surface (a nose stripe, a beltline, a skirt), because each one would need its own material, splitting the mesh and adding a draw call apiece. That’s the point where Paint Maps take over: one material, one draw call, up to four colors placed by a control texture.
Color values
Section titled “Color values”A bare hex string, "1B3D2F" with or without a leading #, sets the color and leaves the material’s authored finish alone.
The paint-material functions metallic(#hex, m[, s]) and smooth(#hex, s) also work here, and additionally override the surface finish, but only on materials using one of the car paint shaders. On any other material, the color still applies and the finish override is ignored.
If a style resolves no color for a group’s class at all, the material is set to flat gray. An unexpectedly gray car almost always means the selector didn’t match. Check that the CarStyle targets Renderer.<styleClass> with the same class the component declares. (Matching is case-insensitive, so that isn’t the culprit.)
colorGamma
Section titled “colorGamma”Some models look washed out or muddy under a raw style color, depending on how their textures were authored. colorGamma corrects for this per group, raising each RGB channel to the power 1 / colorGamma. Values above 1 brighten, values below 1 darken, and the default 1 applies the color unchanged. Tune it once to suit the model’s textures.
Letting players choose the color
Section titled “Letting players choose the color”Applying a whole paint scheme is built in; to expose a single zone’s color as its own control in the Customize window, register a style property from your pack’s Main.rl:
from railroader import ui
func onWorldLoad(ctx) { ui.registerStyleProperty("myhandle/my-car", "Renderer.body", "color", "Body Color", "paint")}The "paint" editor gives a color swatch plus a metallic knob; "color" gives a plain swatch. The value the player picks is stored as an inline override on that car instance, layered over the base paint scheme. See Style Properties and the style cascade.
See also
Section titled “See also”- Styles — the full selector and property reference
- RendererGroup — the component’s field reference
- CarStyle — defining a paint scheme
- Paint Maps — multiple colors on a single zone
- Style Properties — player-editable color controls
© 2026 Giraffe Lab LLC. All rights reserved.