Multi-color FDM prints usually mean a filament change every layer and a purge tower bigger than the part itself. PLAinterly skips all that. By treating the print as a single body with variable thickness — a dark base, then a heightmap of translucent light filament on top — you get a posterized rendering of any photo with exactly one global filament swap. The result reads like a 4–8 tone print but slices like a normal single-color object with a pause. Tuned for the Bambu X1C, but the technique works on any dual-material setup (including manual filament changes). Bookmarks are the obvious first target (and the default geometry in the repo), but the same approach makes coasters, plaques, ornaments, fridge magnets, gift tags — anything roughly flat where you want a photo embedded.
What you'll need- A Bambu X1C with AMS (or any dual-material FDM printer with a "pause and swap filament" feature)
- One spool of dark filament (black, dark grey, navy — anything opaque)
- One spool of light filament (white, ivory, light grey — slight translucency is good)
- Python 3.10+
- OpenSCAD (free, https://openscad.org)
- BambuStudio or your printer's slicer
- A photo. High contrast subjects work best — portraits, silhouettes, line art, logos.
git clone https://github.com/aschokking/plainterly.git
cd plainterly
pip install Pillow numpy
OpenSCAD just needs to be on your PATH (or installed in the default location on Windows). The script auto-detects it.
Step 2: Pick a good source imageThe technique posterizes into 4–8 tonal bands, so images with strong tonal structure shine. Things that work:
- Portraits with clear shadows (think classic black-and-white photography)
- Silhouettes against open sky
- High-contrast nature shots — trees against sunsets, mountain ridgelines
- Logos, line art, simple illustrations
Things that struggle:
- Flat midtone scenes (overcast landscapes, indoor snapshots)
- Lots of fine detail at the same brightness level
- Faces lit flat — convert to side-lit or boost contrast first
python bookmark_heightmap.py my_photo.jpg --out out
That produces three files in out/:
heightmap.png— the posterized grayscale that drives the printbookmark.scad— the OpenSCAD modelbookmark.3mf— a BambuStudio project with the filament change pre-baked (if OpenSCAD is on your PATH)
Open heightmap.png and look at it before printing. If it's muddy or features are lost, tweak:
--levels 4through--levels 8(fewer levels = chunkier/more graphic, more = subtler)--contrast 1.5to push midtones apart--auto-contrast 2to clip the darkest/brightest 2% before stretching
[photo: heightmap.png example]
Step 4: Open the project in licerDouble-click bookmark.3mf. BambuStudio opens with the model loaded and a filament change already inserted at the correct layer.
If you only have the .scad file (OpenSCAD wasn't found): open it in OpenSCAD, render with F6, Export STL, then import that STL into BambuStudio and add a filament change manually at the layer the script printed in the terminal.
In BambuStudio's filament panel:
- Filament 1 → dark spool
- Filament 2 → light spool
The model is one body — the slicer handles which filament prints below vs. above the swap layer based on the filament-change marker.
Step 6: Slicer settings- Layer height: match what you used in
--layer(default 0.08mm). The script assumes uniform layers. - Initial layer height: also 0.08mm, otherwise Bambu rounds the swap to the nearest layer boundary (usually fine, can be off by one).
- Supports: OFF.
- Infill: doesn't really matter at this thickness — 15% is fine.
Slice, send, watch. The print pauses partway up for the filament swap (the X1C handles this automatically with the AMS — you don't have to be there). Total print time is usually 30–60 minutes depending on size and layer height.
Step 8 (optional): Calibrate for your filamentDifferent light filaments have different translucency. If the brightest band of your print looks the same as the second-brightest, your filament is more opaque than the default model assumes — drop --opacity-mm from 0.30 to 0.20 and re-run. If the gradient looks linear with no perceptible "saturation" at the top, your filament is more translucent — increase --opacity-mm or add more --levels.
The repo includes make_calibration_gradient.py for printing a stepped reference strip with one band per layer count, which makes this dial-in easier.




Comments