SLA / DLP / LCM
How to price vat photopolymerisation parts (SLA, DLP, MSLA, LCM) from resin volume and machine time, including the SLA-specific cross-section scaling adjustment.
Why this process is unique
Vat photopolymerisation (SLA, DLP, LCD, LCM) cures liquid resin layer by layer using UV light. Cost has two components:
- Resin: sold by the litre, significantly more expensive than FDM filament
- Machine time: layer count × time per layer × cost per hour
Supports are mandatory for most prints. They consume resin and add post-processing time (removal, sanding). Unlike FDM, supports in vat processes are separate structures added by the slicer - estimate from the convex hull gap.
SLA vs DLP - the critical pricing distinction
SLA and DLP cure resin fundamentally differently. This changes how time per layer behaves and has direct consequences for pricing.
| SLA | DLP / MSLA / LCM | |
|---|---|---|
| How it works | Laser traces the contour and fill of each layer point by point | UV projector or masked screen exposes the entire layer simultaneously |
| Time per layer | Scales with cross-sectional area - more to scan = longer | Fixed exposure time regardless of layer area or part count |
| Packing more parts | Each additional part increases cross-section to scan, so each layer takes longer | Nearly free - same exposure time whether 1 or 10 parts fill the plate |
timePerLayer in your equation | Calibrate per typical part size, or calculate from area | Set as a fixed constant for your machine and resin |
For SLA: print time grows with both layer count and the amount the laser has to trace per layer. Nesting more parts on the build plate can reduce per-part machine cost, but only up to a point - each added part increases the cross-section to scan, making every layer slower. On large or dense plates the benefit of nesting shrinks significantly.
For DLP / MSLA / LCM: the projector flashes the entire layer at once. Packing more parts onto the plate does not increase time per layer at all - nesting is almost always a win.
Warning
The algorithm below uses a fixed timePerLayer - correct for DLP/MSLA/LCM. For SLA, see the note at the end of the algorithm section.
Core methodology
Resin volume: actual part volume + estimated support volume + waste
Print time: numberOfLayers × timePerLayer. The part is usually oriented at an angle (45°) to minimise layer count and support contact area.
Orientation effect: at 45°, the effective print height = longest dimension × cos(45°) ≈ longest × 0.707
Numerical example
Part: 50 × 30 × 80mm bracket, volume = 30,000 mm³, convexHullVolume = 35,000 mm³
| Parameter | Value |
|---|---|
resinCostPerLiter | €95 |
machineCostPerH | €12 |
| Layer height | 0.06 mm |
| Time per layer | 25 s |
Resin volume:
- Part: 30,000 mm³
- Supports: (35,000 − 30,000) × 0.10 = 500 mm³
- Waste: 30,000 mm² × area ≈ 0 (simplified)
- Total: ~30,500 mm³ = 0.0305 L
- Resin cost: 0.0305 × 95 = €2.90
Print time:
- maxDim = 80mm, at 45°: height = 80 × 0.707 = 56.6mm
- Layers: ceil(56.6 / 0.06) = 944
- Time: 944 × 25 / 3600 = 6.55 hours
- Machine cost: 6.55 × 12 = €78.60
Unit price (1 part): ~€81.50
Complete algorithm
const { material, width, height, length, volume, area, convexHullVolume } = specification
const { quantity } = requisition
// Chamber limits
const CHAMBER_LENGTH = 220
const CHAMBER_WIDTH = 125
const CHAMBER_HEIGHT = 200
const LAYER_HEIGHT_MM = 0.06
const resinCostPerLiter = material.variables['resinCostPerLiter']
const machineCostPerH = material.variables['machineCostPerH']
// Resin volume: part + supports + waste
const supportVolumeMm3 = (convexHullVolume - volume) * 0.10
const wasteVolumeMm3 = area * 0.05
const totalVolumeLiters = (volume + supportVolumeMm3 + wasteVolumeMm3) / 1_000_000
const materialCost = round(totalVolumeLiters * resinCostPerLiter, 2)
// Print time: parts are tilted 45° to minimise Z height
const maxDimension = Math.max(width, height, length)
const heightAt45Deg = maxDimension * Math.cos(45 * (Math.PI / 180))
const numberOfLayers = Math.ceil(heightAt45Deg / LAYER_HEIGHT_MM)
const timePerLayer = variable('timePerLayer', 25) // seconds, override for different resins
const printTimeHours = variable('printTime', round(numberOfLayers * timePerLayer / 3600, 2))
const machineCost = round(printTimeHours * machineCostPerH, 2)
// Pricing
const getDiscount = createBands({ 5: 0.95, 10: 0.90, 20: 0.875, 40: 0.85, 80: 0.825 }, 1.0)
const baseCost = (materialCost + machineCost) * getDiscount(quantity)
const unitPrice = variable('unitPrice', round(baseCost, 2))
const oversized = length > CHAMBER_LENGTH || width > CHAMBER_WIDTH || height > CHAMBER_HEIGHT
done(unitPrice, printTimeHours, oversized)Material variables: resinCostPerLiter, machineCostPerH
Adapting for SLA
For SLA, replace the fixed timePerLayer with one that scales with cross-sectional area. The laser scans at a fixed speed, so more area = more time.
Estimating average cross-section:
The ratio volume / minBoundingBoxVolume tells you what fraction of the bounding box is actually solid. Apply that fill ratio to the XY footprint of the bounding box and you get the average cross-section the laser has to trace per layer:
avgCrossSection = (length × width) × (volume / minBoundingBoxVolume)
= volume / heightThis simplifies neatly: average cross-sectional area = total volume ÷ effective print height. No surface area involved - surface area is the outer skin, not the per-layer slice.
// Average cross-section: fill ratio applied to XY bounding footprint
// Equivalent to volume / heightAt45Deg — derived from bbox fill ratio
const fillRatio = volume / minBoundingBoxVolume
const avgCrossSectionMm2 = length * width * fillRatio // = volume / height
// Laser scan speed in mm2/s — set as a material variable for your machine
const scanSpeedMm2PerSec = material.variables['scanSpeedMm2PerSec'] // e.g. 1000
const timePerLayerSLA = variable('timePerLayer', round(avgCrossSectionMm2 / scanSpeedMm2PerSec, 2))
const printTimeHours = variable('printTime', round(numberOfLayers * timePerLayerSLA / 3600, 2))For SLA you should also treat nesting conservatively: each additional part on the plate adds its cross-section to every shared layer, so packing many large parts can make the machine cost per part increase rather than decrease.
When to use this
All UV vat processes. For DLP / MSLA / LCM: use the main algorithm as-is with a fixed timePerLayer. For SLA: use the adapted version above with scanSpeedMm2PerSec as a material variable.
Last updated on
Large Format FDM
How to adapt the time-based FDM pricing model for large format pellet extrusion, gantry, and SCARA systems, including adjusted constants and a complete algorithm.
Metal - LPBF (SLM / DMLS)
How to price metal Laser Powder Bed Fusion parts using a machine-time-dominated model based on build volume and laser build rate.