OpenVCAD

Programmable geometry and spatial material properties for multi-material fabrication

OpenVCAD is a Python library for designing objects whose geometry and material properties change from point to point, and for compiling those designs into files that printers and simulation tools can use. It is developed in the Matter Assembly Computation Lab at the University of Colorado Boulder.

Three printed objects: a green and blue lattice squeezed between two fingers, a gray plaque spelling OpenVCAD in a pink-to-yellow gyroid lattice, and an orange teapot shading to pink at the base beside a coin.
Printed objects designed in OpenVCAD: a lattice with graded hardness, the OpenVCAD name built from a color-graded gyroid, and a Utah teapot with a three-color gradient.

Designing with OpenVCAD

An OpenVCAD object has two parts. Its geometry is an implicit shape, a signed distance field that is negative inside the object, zero on its surface, and positive outside. Shapes are combined with Boolean operations and transforms, or imported from meshes, CAD models, and scans. Its attributes are named fields attached to that geometry, such as color, stiffness, Shore hardness, temperature, or the mix of printing materials. An attribute can depend on position (x, y, z) and on d, the distance to the surface, so it can change along an axis, around a center, or inward from the surface.

Designs are ordinary Python. This part is a cube intersected with a sphere, with three cylinders removed:

import pyvcad as pv
import pyvcad_rendering as viz

cylinder = pv.Cylinder(pv.Vec3(0, 0, 0), 2.0, 9.0)

root = pv.Difference(
    pv.Intersection(
        pv.RectPrism(pv.Vec3(0, 0, 0), pv.Vec3(8, 8, 8)),
        pv.Sphere(pv.Vec3(0, 0, 0), 5.5),
    ),
    pv.Union(
        cylinder,
        pv.Union(
            pv.Rotate(90, 0, 0, cylinder),
            pv.Rotate(0, 90, 0, cylinder),
        ),
    ),
)

viz.Render(root)
Gray rounded cube with a round hole through each face.
Output of this code, rendered by OpenVCAD.

Attributes make a design multi-material. Here two printing materials blend from the bottom of a cylinder to its top:

import pyvcad as pv
import pyvcad_rendering as viz

materials = pv.default_materials

# A cylinder 12 mm wide and 20 mm tall, from z = -10 to z = 10
root = pv.Cylinder(pv.Vec3(0, 0, 0), 6.0, 20.0)

# Blend two materials from the bottom to the top
top = "(z + 10) / 20"
fractions = pv.VolumeFractionsAttribute([
    (f"1 - {top}", materials.id("cyan")),
    (top, materials.id("magenta")),
])
root.set_attribute(
    pv.DefaultAttributes.VOLUME_FRACTIONS, fractions
)

viz.Render(root, materials)
A cylinder shading from cyan at the bottom through purple to magenta at the top.
Output of this code: cyan material at the bottom blends into magenta at the top.

Nothing is turned into voxels or toolpaths while you design. When a design is ready, a compiler samples its fields for a particular process. Translation models can convert design intent into what a machine needs, for example a target hardness into material fractions for an inkjet printer or into nozzle temperatures for foaming filament. Because the design stores properties rather than printer settings, the same source can be compiled for more than one process. The Getting Started guide and the Functional Grading Guide cover these ideas step by step.

Supported outputs

Process Output OpenVCAD component
Multi-material inkjet (material jetting) Image stack assigning a material to every voxel Material inkjet compiler
Full-color inkjet Image stack in cyan, magenta, yellow, black, white, and clear Color inkjet compiler
Vat photopolymerization (LCD, DLP, masked SLA) Grayscale exposure mask for each layer Vat photo compiler
Multi-material vat printing Synchronized grayscale image stacks for two- or four-vat systems Multi-material vat compiler
Filament printing through a slicer PrusaSlicer or OrcaSlicer .3mf project with per-region settings, process states, or color and material mixing Slicer project compilers
Filament printing, direct G-code with gradient-aware toolpaths for mixing extruders, tool changers, and foaming filaments VCAD-Slicer (separate repository)
Finite element simulation Meshes with material properties: .xdmf with .h5, or Abaqus .inp Simulation compiler

3D-printed pre-surgical planning models

Medical scans record a value at every point inside the body. OpenVCAD reads these volumes directly and maps each value to a printable property, so internal structure carries through the whole print instead of being reduced to a few surface meshes. The models in this section are research prints and have not been clinically validated.

Photograph of two printed halves of a brain scan. Tissue appears in blues and greens, and an oval tumor in yellow-green is visible in each half.
Printed model. A brain scan with a tumor (yellow-green), printed in two halves on a full-color inkjet printer. Scan values set the color and opacity throughout the volume.
OpenVCAD preview of the same example, moving through the head one layer at a time. Scan data: a 3D Slicer sample dataset.

One scan, two models

A single CT scan of a pair of feet can be translated in different ways. For a visual model, radiodensity becomes color, so bone and soft tissue look different. For a mechanical model, radiodensity becomes target stiffness and toughness, which OpenVCAD converts into mixtures of soft and rigid printing materials. The bones in that print are rigid, while the skin and heel stay soft.

Printed pair of feet in skin tones, with darker bones visible beneath the surface.
Visual model. Radiodensity mapped to color.
Printed pair of feet in a nearly uniform tan color.
Mechanical model. Radiodensity mapped to stiffness and toughness, then printed with soft, rigid, and liquid materials.

CT phantoms

A phantom is a printed object that is meant to be scanned. For this separate print, radiodensity from a foot CT scan was mapped to mixtures of a radiopaque resin (RadioMatrix), which blocks more X-rays, and a conventional black resin (VeroBlack). The printed phantom was then CT-scanned. The comparison is visual; it is not a quantitative validation of the phantom.

A dark, glossy printed phantom shaped like a pair of feet.
The printed phantom.
Gray CT image of a pair of feet seen from below, from the original scan. Original CT scan
Gray CT image of the printed phantom seen from below, showing a similar arrangement of bones. CT scan of printed phantom
Bottom view. Drag the divider, or focus it and use the arrow keys.

More in the volume data guide and on the lab's pre-surgical planning research page.

Metamaterials and lattice design in Python

Lattices and other architected materials are built from repeating unit cells. In OpenVCAD a lattice is ordinary Python: choose a cell, lay the cells out in a region or across a surface, and set properties for the whole structure, for each cell, or for each strut. The lattice tools are included in the OpenVCAD package and are free to use under its noncommercial license.

Catalogue of gray unit-cell renders arranged in three labeled rows: nine TPMS surfaces, ten strut lattices, and seven face and plate lattices.
TPMS surfaces
Gyroid, Fischer-Koch S, F-RD, I-WP, Lidinoid, Neovius, Schwarz-D, Schwarz-P, and Split-P unit cells.
Strut lattices
Cubic, BCC, FCC, Kelvin, octet, diamond, isotruss, fluorite, hex-prism edge, and re-entrant hex unit cells.
Face and plate lattices
Folded plate, simple-cubic plate, body-centered cubic, FCC or octet plate, cubic plus octet plate, hexagonal honeycomb, and re-entrant honeycomb cells.

Scroll each row sideways, or open the full catalogue.

Built-in unit cells in three families: triply periodic minimal surfaces (TPMS), strut lattices, and face or plate lattices. Custom cells can also be written in Python.

A gyroid lattice filling a 30 mm cube (units are millimeters), three cells on each side:

import pyvcad as pv
import pyvcad_metamaterials as mm
import pyvcad_rendering as viz

cell_map = mm.rectangular_cell_map(
    (pv.Vec3(-15, -15, -15), pv.Vec3(15, 15, 15)),
    cells=(3, 3, 3),
)
root = mm.gyroid(cell_map, wall_thickness=1.6)

viz.Render(root)

Replacing mm.gyroid with another cell, such as mm.schwarz_p, changes the lattice.

Gray gyroid lattice filling a cube, with curved interconnected walls.
Output of this code, rendered by OpenVCAD.

Grading material inside each cell

Properties can follow the cell pattern instead of the overall shape. In this workflow a hardness field is defined on one unit cell, softer toward the strut tips and harder at the center, then repeated through the lattice and printed. Because the field belongs to the cells, it moves with them when a lattice is mapped onto a curved shape.

1. Define geometry
Gray render of a single strut unit cell with struts radiating from its center.
2. Assign hardness
The same unit cell colored green at the center and blue at the strut tips.
3. Tile
Several graded cells joined into a lattice, green at the joints and blue between them.
4. Compile and print
The printed green and blue lattice compressed between two fingers.
In the renders, hardness runs from softer (blue) to harder (green). The printed lattice is shown compressed by hand.

The same idea on a BCC lattice. With coordinates="cell", x, y, and z run from 0 to 1 across each unit cell, so one expression repeats in every cell. Here hardness is highest at each cell's center:

import pyvcad as pv
import pyvcad_metamaterials as mm
import pyvcad_rendering as viz

cell_map = mm.rectangular_cell_map(
    (pv.Vec3(-15, -15, -15), pv.Vec3(15, 15, 15)),
    cells=(3, 3, 3),
)
root = mm.bcc(cell_map, beam_radius=0.9, node_radius=1.2)

# 0 at each cell's center, 1 at its corners
r = "sqrt((x-0.5)^2 + (y-0.5)^2 + (z-0.5)^2) / 0.866"
hardness = pv.FloatAttribute(f"85 - 50 * {r}")

shore = pv.DefaultAttributes.SHORE_HARDNESS
root.set_attribute(shore, hardness, coordinates="cell")

viz.Render(root)
Strut lattice colored dark blue at the cell corners and yellow near each cell center.
Output of this code, colored by Shore A hardness: dark blue is softer, yellow is harder.

Lattices that follow a surface

Cells do not have to sit on a rectangular grid. A conformal map lays them out over a curved surface, so the lattice follows the shape of the part.

A blue wavy surface on a white background.
Surface.
Lattice mapped onto that surface, cell by cell.

Geometry and material grading together

Both kinds of grading can be combined. In this gyroid wing, the cells shrink toward the tip to keep detail in the thinner sections, while two materials blend along its length.

Render of a gyroid-filled wing shading from blue at the root to red at the tip, annotated with arrows labeled cross-graded materials and decreasing gyroid cell size.
Design render. Blue marks the stiffer material and red the more flexible one.
A printed red and yellow wing with gyroid openings, held at its root.
The printed wing.

Guides: metamaterials and conformal lattices.

Functionally graded toolpaths and slicer settings

On filament (FFF) printers, much of a part's behavior comes from how it is printed: the blend of filaments in the nozzle, nozzle temperature and flow, infill density, and surface texture. OpenVCAD treats these process parameters and slicer settings as spatial fields, just like color or hardness, and compiles them into files that filament printers and slicers already use.

Gradient-aware toolpaths

Some parameters cannot change instantly. A mixing extruder needs time to flush one blend before the next, and foaming filament responds to a temperature change with a delay. The gradient-aware slicer cuts each layer's toolpaths along contours of the design field and orders them so the printer follows the gradient instead of jumping across it, then writes G-code directly from the OpenVCAD design.

Three views of a printed vase with wavy walls, graded between blue and yellow in several directions.
A vase with a multi-axis blue-to-yellow gradient, printed on a two-filament mixing extruder. Three views of one print.
Figure comparing the vase design render with prints from a mixing system, a five-toolhead system with discrete green, blue, and yellow bands, and a black foaming-filament print, each with a close-up.
The same design (a) printed with (b) a two-filament mixing extruder, (c) a five-toolhead printer using discrete filament colors, and (d) a single foaming filament, where nozzle temperature changes the local density; lighter regions foamed more. From the 2025 toolpath paper.

Slicer settings as fields

Mainstream slicers already handle toolpath planning, supports, and printer profiles. The slicer-project compiler keeps that work in the slicer: it divides each field into regions, exports a matching sub-mesh for each one, and writes a PrusaSlicer or OrcaSlicer project in which every region carries its own settings. Settings that shape toolpaths, such as infill density or fuzzy skin, are applied per region; machine states, such as nozzle temperature and flow, change when the printer crosses a region boundary.

Render of a rectangular bar shaded from dark purple at one end to yellow at the other.
Design. Infill density increases along the bar, from dark to yellow.
PrusaSlicer preview of the bar with its top removed, showing coarse infill at one end becoming progressively finer toward the other.
PrusaSlicer preview of the compiled project, with the top cut away: sparse infill becomes dense.

From the example 02_infill_density_bar.py.

Texture and process state together

In this bunny, both Shore hardness and surface texture (fuzzy skin) are defined as fields. OpenVCAD converts hardness into nozzle temperature and flow rate for a foaming TPU filament, then writes a PrusaSlicer project whose regions carry the matching temperature, flow, and fuzzy-skin settings. In the print, lighter areas are softer and darker areas are harder.

Render of a bunny colored yellow on the body, blue at the ears and eyes, and blue-gray at the tail.
Design, colored by target hardness: 85A, 65A. Rendered from the example foaming_fuzzy_bunny.py.
Photograph of a blue printed bunny with a fuzzy textured surface, smoother ears, and a fuzzy tail.
Printed in foaming TPU. Photograph from the 2026 slicer-project preprint.

One hardness design, two printing processes

The same kind of hardness field can also be compiled for an entirely different process. This insole design asks for a soft 65A heel, firmer 85A arch and side support, and 75A elsewhere. From that one field, OpenVCAD computes nozzle temperature and flow rate for foaming TPU on a filament printer, and soft and rigid material fractions for an inkjet printer.

Designed hardness Insole render colored by hardness: dark blue at the heel, yellow at the arch and side, and blue-gray elsewhere, with a 65A to 85A scale.
Filament print (foaming TPU) Blue printed insole with a darker band along the arch.
Inkjet print Brown printed insole.

In the filament print, darker blue means less foaming and a stiffer region; the inkjet print's color does not show material composition. Full figure with the computed fields. Guides: slicer projects and attribute translation.

Simulation-driven design

OpenVCAD exports designs as finite element meshes that carry their material properties, and it reads solver results back in. An imported result, such as displacement or strain energy, becomes one more attribute on the original design. It can then decide where stiff material goes, how thick lattice struts are, or which print settings a region receives, not only what shape the part takes.

1. Simulate Bicycle seat colored by simulated displacement, blue at the supported ends and red in the unsupported middle.
2. Import as a field The same seat rendered with a blue and red material distribution, red concentrated in the middle.
3. Set print settings PrusaSlicer preview of the seat showing regions with different infill densities.
4. Print Green printed seat cut open, showing a lattice-like infill that is denser in some regions, beside a coin.
A bicycle seat supported only at its ends was simulated under a rider's load, and the middle deflected the most. The displacement result was imported into OpenVCAD and mapped to a mix of stiff and soft material, with more stiff material (red) where the seat deflected. For a filament printer, that field was split into four regions, each given its own infill density in PrusaSlicer: stiffer regions got denser infill. The seat was printed in a single TPU filament; the cutaway shows the varying infill. From the 2025 Python paper.

The import step: with the solver's node positions (points), tetrahedra (cells), and per-node result (energy) loaded as arrays, the result is attached to the original design like any other field:

results = pv.UnstructuredFieldDataset.from_tetrahedra(points, cells)
results.add_point_scalar("strain_energy", energy)

# The result becomes an ordinary attribute on the original design
strain_energy = results.float_attribute("strain_energy")
part.set_attribute("strain_energy", strain_energy)

# ...and can drive material, geometry, or process fields
stiff = strain_energy.normalize(0.2, 1.2).clamp(0.0, 1.0)

Guides: simulation-driven design and the simulation compiler.

Volume printing

Here, volume printing means printing sampled 3D data, such as a CT scan or a simulation, so that color and transparency vary through the whole object and not only on its surface. OpenVCAD maps each data value to a color and an opacity and compiles the result for a full-color inkjet printer.

Front of an Apple Vision Pro headset with its dark, glossy glass. Original device
Front of the clear printed model of the headset, with internal circuit boards and components visible in green and copper tones. 3D print of CT data
Apple Vision Pro, front view. CT-derived color and opacity reveal internal electronic and mechanical structures in the print, including circuit boards, wiring, cameras, and the motor that adjusts the spacing of the displays. Drag the divider, or focus it and use the arrow keys. Full-size photos: original device, print.

Fire simulation

A fire simulation stores temperature and smoke density at each point. In this example, temperature sets the color and density sets the opacity.

OpenVCAD render of the fire example's color and opacity field.
Photograph of a clear rectangular print containing orange flames at the base and white smoke rising above them.
Printed volume.

Fire data: the fire.vdb example dataset from the OpenVDB project. More in the volume data guide.

Inspecting designs

The OpenVCAD Renderer opens from Python with viz.Render(root). It shows the design tree next to the model, colors the model by any of its attributes, and includes tools to clip through it, probe values, and plot values along a line, so a design can be checked before it is compiled. Below, it shows the graded lattice from the metamaterials section, colored by hardness.

Screenshot of the OpenVCAD Renderer window. A design tree panel is on the left, a strut lattice colored from dark blue to yellow with a Shore hardness scale bar is in the center, and an inspector panel with surface, colormap, and range settings is on the right.
The OpenVCAD Renderer on macOS. Select the image to enlarge it.

More examples

Printed screwdriver with a shaft shading from pink at the tip through orange to yellow, and a translucent handle held in a gloved hand.
Graded screwdriver. A mesh graded across three materials, giving a multi-color shaft and a soft-touch handle.
Close-up of a printed gear, blue in the middle and shading to magenta at the teeth.
Gear. A radial color gradient from the center to the teeth.

Publications

If you use OpenVCAD in your work, we ask that you cite the relevant papers below. BibTeX for each entry is on the publications page.

  1. PreprintFunctionally Grading the Slicing Process by Compiling Design Intent into Slicer Projects Charles Wade, Devon Beck, Robert MacCurdy · arXiv preprint, 2026

    Compiles volumetric designs into ready-to-slice PrusaSlicer and OrcaSlicer projects, so spatial fields control slicer settings, printer state, and color or material mixing.

    PDFarXivCitation
  2. PreprintDesign-Intent Compilation for Heterogeneous Fabrication Charles Wade, Devon Beck, Robert MacCurdy · arXiv preprint, 2026

    Describes designs as typed spatial attributes and translates them into material, process, or slicer outputs for material jetting and material extrusion without rewriting the design for each printer.

    PDFarXivCitation
  3. Implicit Modeling for 3D-printed Multi-material Computational Object Design via Python Charles Wade, Devon Beck, Robert MacCurdy · Proceedings of the 10th ACM Symposium on Computational Fabrication, 2025

    Introduces the Python API, multi-material lattice design, and links to finite element analysis for simulation-informed designs.

    PDFarXivCitation
  4. Implicit Toolpath Generation for Functionally Graded Additive Manufacturing via Gradient-Aware Slicing Charles Wade, Devon Beck, Robert MacCurdy · Additive Manufacturing, 2025

    Generates filament-printer toolpaths directly from OpenVCAD's material gradients, allowing continuous changes in mixture, nozzle temperature, and other print parameters.

    PDFDOICitationCode
  5. OpenVCAD: An open source volumetric multi-material geometry compiler Charles Wade, Graham Williams, Sean Connelly, Braden Kopec, Robert MacCurdy · Additive Manufacturing, 2024

    The original OpenVCAD paper: implicit geometry and material distributions compiled for multi-material inkjet printing.

    PDFDOICitation

Using OpenVCAD

Install

OpenVCAD is installed from PyPI into a Python virtual environment:

python -m pip install OpenVCAD

See the installation guide for supported platforms and setup.

License

OpenVCAD is free for noncommercial academic and personal research under the University of Colorado's license. Commercial use requires a separate license.

Contact

For questions, collaborations, or commercial licensing, contact Charles Wade at charles.wade@colorado.edu.