An experiment with multicolour OpenSCAD

Sometimes when you can’t find quite the right tool, you have to create it yourself… today was one of those days…

I can’t see myself ever creating 3D objects with a GUI editor, so I’ve been learning OpenSCAD and it seems to be quite sufficient for most of my needs so far, but most recently I’ve wanted to apply some text to my models in a second colour, and the process at the moment seems to involve more manual steps than I’m comfortable with, so I looked into the problem and I think I’m on track towards a solution…

Although OpenSCAD can create objects with multiple colours, and Bambu Studio can handle .3mf files with multiple colours, there’s a break somewhere in the tool chain workflow that stops you going from one to the other without cumbersome manual steps. So I’ve been looking at the various file generation options in OpenSCAD and found that .csg files contain the colour information and are easy to manipulate because they’ve mostly been pre-flattened by OpenSCAD from the .scad original.

I’ve hacked up a quick parser for .csg format (which was mostly well documented online, the only missing info that I had to guess at was the format of comments in the files - nothing that OpenSCAD generated included comments) and I’ve written a filter that detects the ‘color()’ modifier and excises anything that doesn’t match a given colour from the file. So by running this once and having the code generate multiple files with each containing a single colour, I don’t need to manually separate the colours at the OpenSCAD source level and create multiple exports. There’s a possibility that the simple algorithm of just removing any objects preceded by a ‘color()’ statement may not be sufficient (e.g. in cases where sub-objects switch back to the selected colour) but I’m hoping that the simplified structure of csg files as opposed to scad files means that this isn’t going to occur in practise - it certainly hasn’t in my tests so far. Worst-case scenario is that users will just have to be careful about marking components that end up in the final object with their colour and not marking components with a colour if they are only used as modifiers to subtract from other pieces - but so far what I’ve seen suggests that that is not going to be a necessary restriction. The only tricky thing I had to be careful about was rather than actually deleting coloured objects, I had to replace them with empty geometry having zero volume, so that operations such as ‘difference’ wouldn’t accidentally replace their first parameter (the thing that was being subtracted from) by one of subsequent objects which was the part that was supposed to be deleted from the first object. (folks who’ve coded OpenSCAD files will appreciate why that’s important.)

Currently I’m still developing the code so it’s still a slightly manual process, but the things I’m doing are all actions that can be automated and the eventual implementation will be a 1-liner (or maybe a drag&drop) conversion from the single export from OpenSCAD in .csg format to a single .3mf file that Bambu Studio can import and print multicoloured without any manual tweaking. It may take me a few weeks to bring it all together because there’s a lot about 3mf files that I still need to learn…

For now I do have to convert those separate .csg files into .3mf files manually, using OpenSCAD (because Bambu Studio can’t read .csg files), and unfortunately the separate component files don’t automatically position themselves correctly within Bambu Studio, so I have to align them manually - but both of those actions should eventually be done by my code.

There’s a work-in-progress snapshot at Index of /OpenSCAD/bicolor-test - some of the comments in the source files are wrong or out of date, just ignore them.

The little test file is actually an interesting experiment in its own right. I was looking at two different ways of putting white text on a black surface like a user did on a box I created ( Vectrex Superbox by gtoal MakerWorld: Download Free 3D Models ) - the first test part was made the traditional way with an all-black surface with 3 or 4 layers of white text embedded at the top of the surface. The second one was an all-white piece with a single layer of black on top with the complement of the text. This version turned out to be better since the white text was white all the way through and was not darkened by having a black substrate beneath it, and has the major advantage of only one layer requiring filament switching rather than the 4 layers with both colours that the embedded white on black text version required. The only downside of this negative mask technique is that the sides of any objects with white text on a black surface will also be in white, but for the items I’m making, that’s OK.

Anyway, this is an experiment I’m reporting, but if it turns into a utility that other people can use, I’ll post the final code here.

(btw I do know that for a real model I should turn the whole thing upside down so that the text is printed on the base plate and not on the top surface like I did here. I just did that to avoid adding unnecessary complications to the simple test file)

1 Like

Are you creating your color objects as top level objects (not in a module) in OpenSCAD? It preserves top level objects without combining them, and then you can open the file (STL or 3MF) with all the pieces in place and assign color filaments.
I posted multicolor bookmark models using OpenSCAD (or the customizer) that show this.

If you could point me at one of your .scad files that I could look at to see what you mean and test with, I’d be grateful. Thanks. btw I can’t upload the scad file that I used for the test above, so I’ll put it inline below:

echo(version=version());

font = "Liberation Sans";
//["Liberation Sans", "Liberation Sans:style=Bold", "Liberation Sans:style=Italic", "Liberation Mono", "Liberation Serif"]

// all measurements are in mm
layerdepth = 0.4;
width = 25;
height = 10;
depth = 4*layerdepth;

module hello() {
  translate([0,0,-3*layerdepth]) linear_extrude(3*layerdepth) text("HELLOO", size=5);
}

difference() {
  union() {
    translate([1.5,3,0]) union() {

      difference() {
        color("black") translate([-1.5,-3,0]) cube([width,height,depth]);
        translate([0,0,depth]) hello();
      }

      color("white") translate([0,0,depth]) hello();
    }

    translate([1.5,height+3+1.2,0]) union() {

      difference() {
        union() {
          color("white") translate([-1.5,-3,0]) cube([width,height,depth-layerdepth]);
          color("black") translate([-1.5,-3,depth-layerdepth]) cube([width,height,layerdepth  ]);
        }
        translate([0,0,depth]) hello();
      }

      color("white") translate([0,0,depth]) hello();
    }
  }

  color("white") translate([width,0,0]) cube([width, height*2+1.2,depth+0.1]);
}

See Customizable Text Bookmark with Raised Letters by ChrisCox MakerWorld: Download Free 3D Models
or Customizable Bookmark with Text Flag by ChrisCox MakerWorld: Download Free 3D Models
Most of my designs are done in OpenSCAD, and customizable – and usually commented so other people can learn from them.

In your example, you put everything together - so there are no separate objects available at the end, and you can’t assign colors to them (it’s all merged into a single 3D mesh). Leaving separate objects at the top level means that they will be separate meshes in the file, and can be assigned different colors for printing. Assigning colors in OpenSCAD doesn’t translate to STL at all (no color), and will only work in 3MF if they are separate objects at the top level.

1 Like

I’ll have a close look at your scad and 3mf files now. Thanks. (btw I didn’t realise until looking at your links just now that you can upload .scad files to makerworld for users to download. I see now they appear under the “Download STL/CAD files” button. I’ll have to add the .scad files to the models I’ve already uploaded - I had been linking to copies hosted on my own web site…)

(10 minutes later…)

Oh - now I see what you meant. So you just list the different coloured objects separately at the top level and don’t union() them. OK, that’s pretty simple!

Which means I don’t really need a post-processing utility if I create the files that way to begin with - though I still think it might be a useful thing to have if I write it so that it filters out the coloured parts from a single object and rewrites the file as separate objects of each colour; that way we won’t need to put so much effort into keeping them separated. It looks like having separate objects for each colour is easy enough if you’re creating the who thing yourself, but if your model includes components imported from other models or libraries then they’re likely not going to be separated already, so a utility to do the separation might still come in handy. Not that I’m importing anything multicoloured at this point, I’m still at the beginner stage doing basic stuff!

Anyway, thanks for the explanation, and seeing an example made it all fall into place for me.

1 Like

I found GitHub - jschobben/colorscad: Script to produce colorized OpenSCAD models this evening - it’s a project with the same overall idea although a different implementation. It tells me that what I’m doing isn’t entirely crazy at any rate… their approach has some restrictions on the contents of the scad file but until finish writing my code and run their tests on it, I don’t know if my method will need the same restrictions to be applied. Also I found out about a ‘lazy union’ option that may be useful but I haven’t found where it’s enabled in the OpenSCAD nightly build. Maybe it was tried in an earlier version and later removed?

1 Like
                    https://gtoal.com/OpenSCAD/colorsep/

This is a WORK IN PROGRESS snapshot of a utility I’m currently developing, which should eventually allow us to go straight from an OpenSCAD design, to a .3mf file usable in Bambu Studio for printing objects in more than one colour. Creating files that allow multicolour printing at the moment, using the current OpenSCAD and Bambu Studio, does involve a bit of manual tweaking that this is intended to eventually do away with and for now at least reduce.

It’s working sufficiently now that I thought I might ask if anyone here (who has to be able to compile C source code) is interested in alpha-testing it for me. If you are, just copy the files from here (there’s also a zip or a tar archive containing the same files for easier downloading) and type ‘make’ after unpacking them. The executable this creates is called ‘colorsep’ and it takes one parameter: a .csg file - email me at gtoal@gtoal.com if (or rather when) you find problems with the rewritten csg output - if it fails, I need to know in what ways. (And if it works out of the box, I’ld appreciate hearing that too!)

(Note there is quite a bit of code in this directory for building the parser, which came from another project of mine, ‘uparse’ (a Unicode-friendly parser generator) at https://github.com/gtoal/uparse - I included the minimum from that project to build the converter (which needed a CSG parser). It’s a bit of a quick hack and when this project is working I’ll try to repackage it in a way that doesn’t include all the extra code from the parser project… but you won’t have to look at any of the C code at all! (But if you do want to look at the actual code, it’s colorsep-filter.c that does the relevant work.) Anyway, as I said above, everything should just build after you unpack it and type ‘make’.)

I just now (30 June 2026, at 9pm) printed my first test using this method, and I’m pleased to report that it printed correctly and there were no problems in the workflow. Here’s what you’ll need to do to try it out yourself, once you’ve compiled the colorsep binary…

  • Write a .scad script to generate your bi-coloured piece. The colours can be freely mixed within the object - unlike the current method, you don’t have to create separate top-level objects in each colour.

  • Process your .scad file using the ‘nightly’ version of openscad. This is important otherwise you can’t export the multicoloured object correctly.

  • export the result from OpenSCAD as a .csg file. If you use the command-line interface, the command may look something like:

    openscad -o file.csg file.scad

  • run my utility on the .csg file to restructure the .csg file, e.g:

    ./colorsep file.csg > file-updated.csg

  • load the updated .csg file back into openscad and export immediately as a .3mf file - be sure to select the “Use colors from model and color scheme” option on the export screen in the ‘Colors’ section, and also check that “Export color as” is set to “Color” in the ‘Format’ section.
    The command-line version of that may be something like:

    openscad -o file.csg file.scad

    Although I haven’t yet looked into how to set those export parameters from the command line - I’ve been doing this step manually with the GUI for the moment. I expect that once I’ve worked out how to do that, it’ll be more convenient to merge the last 3 steps into a single command-line script. Eventually I’ld like to script the entire process but that’s still some time off.

  • load the .3mf file into Bambu Studio. It’ll be seen as a non-Bambu format file but that’s not
    a problem, just accept it.

  • Now comes the only slightly tricky manual step: right-click on any object at some point that was designed to be in one of the colours, and assign the appropriate filament to each colour in turn. The reason this is tricky is that when you imported the .3mf file, it was all displayed in the one default colour! If you can’t immediately identify a part of the design that you know should be printed in a given colour, you can temporarily slice the uncoloured view to make the paths more visible; then once you’ve assigned the filaments, re-slice it again for printing.

    Note that you only need to assign each filament once per colour - you don’t need to assign it for every distinct part of the object that is going to be printed in the same colour. Every instance of the color() instruction in the scad file that has the same r/g/b values is treated as the same filament (even if the ‘a’ value for the displayed colour transparency was different).

    The manual stage above is pretty much the same as you would have done if you created a .scad file with separate top-level objects; the advantage of this method is that you did not have to manually separate the colours, or mess about with Bambu Studio dropping the different components on to the plate, with you having to manually reposition them so that the coloured pieces are all aligned correctly with each other.
    You may get a warning from Bambu Studio that there are unsupported components, when you run the slicer, but that warning should be ignored as the parts are in fact already in the correct position for printing.

  • At this stage, if you want to, you can re-export the .3mf file from Bambu Studio if you plan to upload the file to Makerworld or give it to other people to print. It should now load with your colours in place and filaments assigned, and should no longer issue the initial warning about not having been created by Bambu Studio.

  • Send the sliced design to the printer!

Feedback - good and bad equally welcomed - to gtoal@gtoal.com. I’m thick-skinned so don’t feel you have to choose your words carefully! Thanks.

Graham

2 Likes

I’ve been using the color command in OpenSCAD (currently using v2026.02.13) to create colored models for Bambu Studio without using additional software or manipulating the model once it is opened in BS.

The trick is to save the .3mf file to disk and import it into BS. Exporting directly into BS currently doesn’t retain the color information. According to some discussion with the OpenSCAD gurus, OS can export color information in a .3mf file in two ways, either as Color or Base Material. These options are available if you Shift-Click the .3mf icon. If Color is used, BS will use the colors from the model. With Base Material it doesn’t. The issue is that the 3d print option doesn’t have configuration options and always exports in Base Material. Once you select a color export option for the .3mf export, it is remembered for future exports.

The second piece is matching the colors to filaments. OS color names map to various RGB settings. BL filaments have different RGB mappings. If the RGB mapping is the same, BS will automatically map colors.

OS tends to use “pure” colors, i.e. color(“Red”) maps to [0xff, 0, 0] but you can see below, that BS uses [0xc1, 0x2e, 0x1f].

I have created color variables in OS that map directly to the BL filaments I use. I found the RGB color codes for their filaments somewhere on the BL website.

// Bambu Lab Filament Colors
// PLA Basic
jade_white = [0xff,0xff,0xff] / 0xff;       
beige = [0xf7,0xe6,0xde] / 0xff;            
light_gray = [0xd1,0xd3,0xd5] / 0xff;       
gray = [0x8e,0x90,0x89] / 0xff;             
magenta = [0xec,0x00,0x8c] / 0xff;          
pink = [0xf5,0x5a,0x74] / 0xff;             
hot_pink = [0xf5,0x54,0x7c] / 0xff;         
orange = [0xff,0x6a,0x13] / 0xff;           
pumpkin_orange = [0xff,0x90,0x16] / 0xff;   
gold = [0xe4,0xbd,0x68] / 0xff;             
sunflower_yellow = [0xfe,0xc6,0x00] / 0xff; 
yellow = [0xf4,0xee,0x2a] / 0xff;           
bright_green = [0xbe,0xcf,0x00] / 0xff;     
bambu_green = [0x00,0xae,0x42] / 0xff;      
mistletoe_green = [0x3f,0x8e,0x43] / 0xff;  
bronze = [0x84,0x7d,0x48] / 0xff;           
cocoa_brown = [0x6f,0x50,0x34] / 0xff;      
brown = [0x9d,0x43,0x2c] / 0xff;            
maroon_red = [0x9d,0x22,0x35] / 0xff;       
red = [0xc1,0x2e,0x1f] / 0xff;              
turquoise = [0x00,0xb1,0xb7] / 0xff;        
cyan = [0x00,0x86,0xd6] / 0xff;             
blue = [0x0a,0x29,0x89] / 0xff;             
cobalt_blue = [0x00,0x56,0xb8] / 0xff;      
purple = [0x5e,0x43,0xb7] / 0xff;           
indigo_purple = [0x48,0x29,0x60] / 0xff;    
blue_grey = [0x5b,0x65,0x79] / 0xff;        
dark_gray = [0x54,0x54,0x54] / 0xff;        
black = [0x00,0x00,0x00] / 0xff;            

OS wants the color components in the range 0..1, whereas the colors on the BL website use the range 0..0xff, hence the division.

If I use the color variables specified above, BS maps directly to the filament color. Just slice and print!

This has been working well for me.

Hopefully the information will be of use to others.

1 Like

I have the code for All Bambu lab filaments including tpu, pla, petg and every other filament if you need it.

Thanks for the offer. I would like that. Currently my file is only for the PLA BASIC filaments as that what I use, but I could update my include file and be ready for different types.

// =============================================
// Bambu Lab Filament Colors for OpenSCAD
// Usage: color(pla_red) { … }
// =============================================

// — PLA Basic —
pla_jade_white = [0xFF,0xFF,0xFF] / 255;
pla_beige = [0xF7,0xE6,0xDE] / 255;
pla_gold = [0xE4,0xBD,0x68] / 255;
pla_silver = [0xA6,0xA9,0xAA] / 255;
pla_gray = [0x8E,0x90,0x89] / 255;
pla_bronze = [0x84,0x7D,0x48] / 255;
pla_brown = [0x9D,0x43,0x2C] / 255;
pla_red = [0xC1,0x2E,0x1F] / 255;
pla_magenta = [0xEC,0x00,0x8C] / 255;
pla_pink = [0xF5,0x5A,0x74] / 255;
pla_orange = [0xFF,0x6A,0x13] / 255;
pla_yellow = [0xF4,0xEE,0x2A] / 255;
pla_yellow_cmyk = [0xFC,0xE3,0x00] / 255;
pla_bambu_green = [0x16,0xC3,0x44] / 255;
pla_cyan = [0x00,0x86,0xD6] / 255;
pla_green = [0x16,0x4B,0x35] / 255;
pla_blue = [0x0A,0x29,0x89] / 255;
pla_purple = [0x5E,0x43,0xB7] / 255;
pla_blue_gray = [0x5B,0x65,0x79] / 255;
pla_black = [0x00,0x00,0x00] / 255;
pla_mistletoe_green = [0x3F,0x8E,0x43] / 255;

// — PLA Galaxy —
galaxy_brown = [0x68,0x4A,0x43] / 255;
galaxy_green = [0x3B,0x66,0x5E] / 255;
galaxy_nebulae = [0x42,0x43,0x79] / 255;
galaxy_purple = [0x59,0x41,0x77] / 255;

// — PLA Glow —
glow_green = [0xA1,0xFF,0xAC] / 255;
glow_yellow = [0xF8,0xFF,0x80] / 255;
glow_pink = [0xF1,0x7B,0x8F] / 255;
glow_blue = [0x7A,0xC0,0xE9] / 255;
glow_orange = [0xFF,0x9D,0x5B] / 255;

// — PETG HF —
petg_yellow = [0xFF,0xD0,0x0B] / 255;
petg_orange = [0xF7,0x54,0x03] / 255;
petg_green = [0x00,0xAE,0x42] / 255;
petg_red = [0xEB,0x3A,0x3A] / 255;
petg_blue = [0x00,0x2E,0x96] / 255;
petg_black = [0x00,0x00,0x00] / 255;
petg_white = [0xFF,0xFF,0xFF] / 255;
petg_cream = [0xF9,0xDF,0xB9] / 255;
petg_lime_green = [0x6E,0xE5,0x3C] / 255;
petg_forest_green = [0x39,0x54,0x1A] / 255;
petg_lake_blue = [0x1F,0x79,0xE5] / 255;
petg_peanut_brown = [0x87,0x57,0x18] / 255;
petg_gray = [0xAD,0xB1,0xB2] / 255;
petg_dark_gray = [0x51,0x51,0x51] / 255;

// — PETG Translucent —
petg_tr_gray = [0x8E,0x8E,0x8E] / 255;
petg_tr_light_blue = [0x61,0xB0,0xFF] / 255;
petg_tr_olive = [0x74,0x8C,0x45] / 255;
petg_tr_brown = [0xC9,0xA3,0x81] / 255;
petg_tr_teal = [0x77,0xED,0xD7] / 255;
petg_tr_orange = [0xFF,0x91,0x1A] / 255;
petg_tr_purple = [0xD6,0xAB,0xFF] / 255;
petg_tr_pink = [0xF9,0xC1,0xBD] / 255;

// — ABS —
abs_beige = [0xDF,0xD1,0xA7] / 255;
abs_lavender = [0x72,0x48,0xBD] / 255;
abs_olive = [0x78,0x9D,0x4A] / 255;
abs_tangerine = [0xFF,0xC7,0x2C] / 255;
abs_azure = [0x48,0x9F,0xDF] / 255;
abs_navy = [0x0C,0x23,0x40] / 255;
abs_white = [0xFF,0xFF,0xFF] / 255;
abs_silver = [0x87,0x90,0x9A] / 255;
abs_red = [0xD3,0x29,0x41] / 255;
abs_orange = [0xFF,0x6A,0x13] / 255;
abs_yellow = [0xFC,0xE9,0x00] / 255;
abs_mint = [0x7A,0xE1,0xBF] / 255;
abs_bambu_green = [0x00,0xAE,0x42] / 255;
abs_blue = [0x0A,0x2C,0xA5] / 255;
abs_purple = [0xAF,0x16,0x85] / 255;
abs_black = [0x00,0x00,0x00] / 255;

// — ABS-GF —
absgf_white = [0xFF,0xFF,0xFF] / 255;
absgf_gray = [0xC6,0xC6,0xC6] / 255;
absgf_yellow = [0xFF,0xE1,0x33] / 255;
absgf_orange = [0xF4,0x84,0x38] / 255;
absgf_red = [0xE8,0x31,0x00] / 255;
absgf_green = [0x61,0xBF,0x36] / 255;
absgf_blue = [0x0C,0x3B,0x95] / 255;
absgf_black = [0x00,0x00,0x00] / 255;

// — ASA —
asa_white = [0xFF,0xFA,0xF2] / 255;
asa_gray = [0x8A,0x94,0x9E] / 255;
asa_red = [0xE0,0x29,0x28] / 255;
asa_green = [0x00,0xA6,0xA0] / 255;
asa_blue = [0x21,0x40,0xB4] / 255;
asa_black = [0x00,0x00,0x00] / 255;
asa_aero = [0xF5,0xF1,0xDD] / 255;

// — TPU for AMS —
tpu_red = [0xED,0x00,0x00] / 255;
tpu_yellow = [0xF9,0xEF,0x41] / 255;
tpu_blue_light = [0x58,0x98,0xDD] / 255;
tpu_blue = [0x00,0x72,0xCE] / 255;
tpu_neon_green = [0x90,0xFF,0x1A] / 255;
tpu_white = [0xFF,0xFF,0xFF] / 255;
tpu_gray = [0x93,0x93,0x93] / 255;
tpu_black = [0x00,0x00,0x00] / 255;

// — TPU 95A HF —
tpu95_white = [0xFF,0xFF,0xFF] / 255;
tpu95_yellow = [0xF3,0xE6,0x00] / 255;
tpu95_blue = [0x00,0x72,0xCE] / 255;
tpu95_red = [0xC8,0x10,0x2E] / 255;
tpu95_gray = [0x89,0x8D,0x8D] / 255;
tpu95_black = [0x10,0x18,0x20] / 255;

// — PA6-GF —
pa6_white = [0xEA,0xEA,0xE4] / 255;
pa6_yellow = [0xFF,0xCE,0x00] / 255;
pa6_lime = [0xC5,0xED,0x48] / 255;
pa6_blue = [0x75,0xAE,0xD8] / 255;
pa6_orange = [0xFF,0x48,0x00] / 255;
pa6_brown = [0x5B,0x49,0x2F] / 255;
pa6_gray = [0x35,0x35,0x33] / 255;
pa6_black = [0x00,0x00,0x00] / 255;

Thanks,

On Apr 4, 2026, at 12:07 AM, Form & Filaments3d via Bambu Lab Community Forum forum-noreply@bambulab.com wrote:

/ =============================================
// Bambu Lab Filament Colors for OpenSCAD
// Usage: color(pla_red) { … }
// =============================================

FWIW, as I was researching for my post, I discovered that there is a simpler way to represent colors in OpenSCAD, hex strings. For example, you can rewrite:

pla_brown = [0x9D,0x43,0x2C] / 255;

as:

pla_brown = "#9D432C";

Mark

I reformatted Form & Filaments 3d’s data to hex strings, sorted by name and added them to my list. Let me know if you need a copy. I can’t figure out how to include the data in my post without the forum changing the text, i.e. converting quotes to smart quoted in a way that can be cut an pasted into something useful. The forum dosen’t allow text attachments.

1 Like

No problem thanks man! Appreciate it.