Turn SVG/PNG artwork into layered, gap-free multicolor STL plates for face-down multi-material 3D printing.
Project description
Turn SVG/PNG artwork into layered, gap-free multicolor STL plates for face-down multi-material 3D printing (toolchanger / MMU).
🌐 Live demo · ⚡ Quick start · 🖥️ Web GUI · 🛠️ How it works
It does what you'd otherwise do by hand in CAD: separate the artwork into its colors, tile them so they share one plane with no overlaps or gaps, extrude a thin colored front shell (the show face that prints against the bed), and stack a single-color backing plate behind it so the back is one clean color.
✨ What you get
- Color separation, done right — every silhouette pixel is assigned to its nearest palette color, so the regions tile with no gaps or overlaps.
- Watertight STLs — one clean, manifold mesh per filament color, plus an optional single-color backing plate so the back of the print is uniform.
- Two ways to drive it — a scriptable CLI and a live browser GUI, both on the same pipeline (no mocks, what you preview is what you print).
- Slicer-ready output — shared origin across all plates, a flat-color preview PNG, and a manifest mapping each color → file → RGB for toolhead assignment.
📸 Screenshots
Drop a logo — colors are detected, each maps to a filament, and the preview is painted with your real assigned colors.
Export — one watertight STL per color, plus the backing plate, as a .zip. |
Dark mode, because of course. |
⚡ Quick start
pip install colorplate # the CLI
pip install "colorplate[auto]" # + auto color detection for rasters (scikit-learn)
pip install "colorplate[web]" # + the colorplate-web browser GUI
From source (for development)
git clone https://github.com/kurenn/colorplate && cd colorplate
pip install -e ".[web]"
Requires a working cairosvg (for SVG input) which needs Cairo system libs.
# SVG: palette auto-detected from the file's fills/strokes
colorplate logo.svg -o out/ --height 180 --backing-color c0
# Explicit, named palette (recommended for clean toolhead mapping)
colorplate logo.svg -o out/ --height 180 \
--palette "dark=#231F1D,rim=#F9CF26,white=#FEFEFE,red=#ED4324" \
--backing-color dark
# Raster with no known palette: quantize to N colors
colorplate art.png -o out/ --colors 4 --backing-color c0
Key options
| flag | meaning | default |
|---|---|---|
--height |
longest in-plane dimension (mm) | 180 |
--front |
colored front-shell thickness (mm) | 1.0 |
--back |
backing thickness (mm) | 2.0 |
--backing-color |
color name for the single-color back (omit = no backing) | none |
--palette |
name=#hex,...; omit to auto-detect |
auto |
--colors |
target colors when quantizing a raster | 4 |
🖥️ Web GUI
A browser front end (the ColorPlate design) drives the same pipeline: drop a
logo, see its colors detected, map each to a filament, set size/thickness/backing,
preview the recolored art live, and download the generated STLs as a .zip.
pip install "colorplate[web]"
colorplate-web # opens http://127.0.0.1:8000 in your browser
# colorplate-web --port 9000 --no-browser
Try it without installing anything: colorplate.spoolr.io
What it does (all real, no mocks):
- Detect — quantizes the rasterized silhouette to up to N colors (the "Max colors" selector), consolidating antialiasing fringes and folding sub-printable slivers into their nearest neighbor, so every region shown is actually printable. Each detected region is pre-mapped to its nearest filament preset.
- Preview — the right panel shows your real artwork recolored with the assigned filaments; it's built from the exact masks used for meshing, so what you see is what the STLs contain. Flip to the 3D view to rotate the actual layered plates (front shells + backing) — the same geometry that gets exported.
- Generate — one watertight STL per distinct assigned filament (regions sharing
a filament are merged), plus an optional single-color backing plate, a flat-color
preview PNG, and a manifest — bundled into a downloadable
.zip.
Endpoints live under /api/*; the static UI is plain React-via-Babel (no build
step). Tiny detail: auto-detection is quantization-based, so a very small distinct
color may merge into a neighbor — bump "Max colors", or use the CLI's --palette
for an exact named palette.
☁️ Deploy (Render)
The GUI + API ship as one container (Dockerfile), with a Render Blueprint
(render.yaml). Render runs a live Python process, so the whole app deploys as a
single web service — no static/host split, no CORS.
- Push this repo to GitHub.
- In Render: New ► Blueprint, pick the repo. It reads
render.yaml, builds the Dockerfile, and injects$PORT(the app binds0.0.0.0:$PORTautomatically). - Open the service URL.
Run the same image anywhere a container runs (Fly.io, Cloud Run, a VM):
docker build -t colorplate .
docker run -p 8000:8000 colorplate # http://localhost:8000
Notes: the free plan (512 MB, sleeps when idle) is fine for typical logos; very large rasters or many colors want more RAM (bump to a paid plan). Upload sessions are held in memory on a single instance, so keep it to one instance (don't scale out).
📦 Output
Per run you get, in the output directory:
*_<color>.stl— one watertight plate per color (front shell, z0..front)*_backing.stl— single-color backing (zfront..front+back)*_preview.png— flat-color preview of the show face*_manifest.json— color → file → RGB map, for assigning toolheads
All STLs share one origin, so in the slicer: load them together, Assemble into one object, assign each part a filament, and print face-down.
🛠️ How it works
RasterLoader SVG -> rasterize (transparent bg) | PNG -> load + bg detect
| => RGBA array + silhouette mask
Classifier assign EVERY silhouette pixel to its nearest palette color
| => per-color masks that tile with no gaps/overlaps
MeshBuilder each mask -> contours (with holes) -> extruded watertight mesh
| scaled px -> mm, at a given thickness + Z offset
PlatePipeline front shells at z0; backing = full silhouette behind; write files
Each stage is a single-responsibility class (raster.py, classify.py,
mesh.py, pipeline.py) so pieces can be swapped or tested in isolation.
📝 Notes
- Thin features (e.g. web strands) must be wider than your nozzle line width at
the chosen
--height; scale up if a preview shows hairline regions. - The front shell must be opaque enough that the backing color doesn't ghost
through; ~1.0 mm (5 layers @ 0.2 mm) is usually fine, bump
--frontif not. - Source artwork must use filled color regions. Pure line-art (strokes only, colors as background showing through) needs a fill pass first.
License
MIT © Abraham Kuri
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file colorplate-0.2.0.tar.gz.
File metadata
- Download URL: colorplate-0.2.0.tar.gz
- Upload date:
- Size: 49.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
986abdef359e95af5c760e64b7eb1ec98837ac0c166ed0ef7ea56c6922d5bcea
|
|
| MD5 |
c0b6c4f2cfa351f378ac53f6326a8dcc
|
|
| BLAKE2b-256 |
ea16558cc00aacf1a3d8b1b97352905e42ee8bff9ad9f23cc0db591555a17f5b
|
Provenance
The following attestation bundles were made for colorplate-0.2.0.tar.gz:
Publisher:
release.yml on kurenn/colorplate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
colorplate-0.2.0.tar.gz -
Subject digest:
986abdef359e95af5c760e64b7eb1ec98837ac0c166ed0ef7ea56c6922d5bcea - Sigstore transparency entry: 1733695457
- Sigstore integration time:
-
Permalink:
kurenn/colorplate@e94fa3882f36ab16cda3fa8663c81544715dc172 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/kurenn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e94fa3882f36ab16cda3fa8663c81544715dc172 -
Trigger Event:
push
-
Statement type:
File details
Details for the file colorplate-0.2.0-py3-none-any.whl.
File metadata
- Download URL: colorplate-0.2.0-py3-none-any.whl
- Upload date:
- Size: 50.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb340d44e4c2df92076b558c2e3a734d864a3c67fe3b18d8b649b4e561afcf81
|
|
| MD5 |
20f1daba6ad7a2cda3d989e814827d5e
|
|
| BLAKE2b-256 |
530c1139ba0c12bee859da0ea4da37f3867b74ad48931a4f32106a2a9798d99d
|
Provenance
The following attestation bundles were made for colorplate-0.2.0-py3-none-any.whl:
Publisher:
release.yml on kurenn/colorplate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
colorplate-0.2.0-py3-none-any.whl -
Subject digest:
fb340d44e4c2df92076b558c2e3a734d864a3c67fe3b18d8b649b4e561afcf81 - Sigstore transparency entry: 1733695566
- Sigstore integration time:
-
Permalink:
kurenn/colorplate@e94fa3882f36ab16cda3fa8663c81544715dc172 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/kurenn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e94fa3882f36ab16cda3fa8663c81544715dc172 -
Trigger Event:
push
-
Statement type: