FDM - Filament Extrusion
How to price FDM/FFF parts by splitting geometry into shell, infill, and supports to estimate print time, which is the dominant cost driver.
Why this process is unique
FDM pricing is dominated by print time, not material cost. A structurally solid part uses similar material volume to the same part printed hollow, but they take very different amounts of machine time.
The key insight is to split the part into three regions that print at different speeds:
- Shell (outer walls): slow, many direction changes
- Infill: fast, long sweeping lines
- Supports: medium speed, low density
Cost = material consumed + machine time × rate
Core methodology
Shell volume: Math.min(volume, area × nozzle × walls) - bounded so thin parts don't produce negative infill
Infill volume: (volume - shellVolume) × infillDensity
Support volume: estimated from the convex hull gap, exposed via variable() for override
Print time: each region's volume ÷ volumetric flow rate, summed and multiplied by overhead
Shell penalty: parts heavy in shell features print slower than their volume suggests
Numerical example
Part: 40 × 40 × 80mm post, 20% infill, volume = 30,000 mm³, area = 14,400 mm²
| Parameter | Value |
|---|---|
| Nozzle diameter | 0.4 mm |
Layer height (precision) | 0.2 mm |
| Wall count | 3 |
| Base speed | 60 mm/s |
| Wall speed factor | 0.5 |
| Infill speed factor | 1.0 |
printerCostPerHour | €8 |
materialCostPerKg | €28, materialDensity = 1.24 g/cm³ |
Shell volume: min(30,000, 14,400 × 0.4 × 3) = min(30,000, 17,280) = 17,280 mm³
Infill volume: (30,000 − 17,280) × 0.20 = 2,544 mm³
Wall time: 17,280 / (0.4 × 0.2 × 60 × 0.5) = 17,280 / 2.4 = 7,200 s
Infill time: 2,544 / (0.4 × 0.2 × 60 × 1.0) = 2,544 / 4.8 = 530 s
Shell penalty: 1 + 0.5 × (17,280 / (17,280 + 2,544))² = 1.41
Print time: (7,200 + 530) × 1.3 × 1.41 / 3600 = 3.7 hours
Material cost: (17,280 + 2,544 + 0 support) / 1,000 × 1.24 / 1,000 × 28 = €0.55
Machine cost: 3.7 × 8 = €29.60
Unit price (1 part, no discount): ~€30.15
Complete algorithm
const { material, width, height, length, volume, area, convexHullVolume, infill, precision } = specification
const { quantity } = requisition
// --- Machine definitions ---
const MACHINES = [
{ name: 'Small desktop FFF', bed: { l: 256, w: 256, h: 256 }, costPerHour: 6 },
{ name: 'Medium desktop FFF', bed: { l: 300, w: 300, h: 300 }, costPerHour: 7 },
{ name: 'Big desktop FFF', bed: { l: 320, w: 320, h: 325 }, costPerHour: 9 },
]
function selectMachine() {
return MACHINES.find(m =>
length <= m.bed.l && width <= m.bed.w && height <= m.bed.h
) ?? MACHINES[MACHINES.length - 1]
}
// --- Constants ---
const NOZZLE_DIAMETER_MM = 0.4
const NUMBER_OF_WALLS = 3
const SUPPORT_INFILL = 0.2
const PRINT_TIME_OVERHEAD = 1.3
// --- Geometry ---
const layerHeight = precision?.value ?? 0.20
const partInfill = infill?.value ?? 0.20
const shellVolume = Math.min(volume, area * NOZZLE_DIAMETER_MM * NUMBER_OF_WALLS)
const infillVolume = (volume - shellVolume) * partInfill
const hullGap = convexHullVolume - volume
const supportVolume = variable('supportVolume', round(hullGap * 0.10, 0))
// --- Material cost ---
const materialDensity = material.variables['materialDensity']
const materialCostPerKg = material.variables['materialCostPerKg']
const mainWeight = ((shellVolume + infillVolume) / 1000) * materialDensity / 1000
const supportWeight = ((supportVolume * SUPPORT_INFILL) / 1000) * materialDensity / 1000
const materialCost = round((mainWeight + supportWeight) * materialCostPerKg, 2)
// --- Print time ---
const baseSpeed = material.variables['baseSpeedNozzle']
const wallSpeedFactor = material.variables['wallSpeedFactor']
const infillSpeedFactor = material.variables['infillSpeedFactor']
const supportSpeedFactor = material.variables['supportSpeedFactor']
const wallTime = shellVolume / (NOZZLE_DIAMETER_MM * layerHeight * baseSpeed * wallSpeedFactor)
const infillTime = infillVolume / (NOZZLE_DIAMETER_MM * layerHeight * baseSpeed * infillSpeedFactor)
const supportTime = (supportVolume * SUPPORT_INFILL) / (NOZZLE_DIAMETER_MM * layerHeight * baseSpeed * supportSpeedFactor)
const shellPenalty = 1 + 0.5 * Math.pow(shellVolume / (shellVolume + infillVolume + 1), 2)
const printTimeHours = variable('printTime', round(
(wallTime + infillTime + supportTime) * PRINT_TIME_OVERHEAD * shellPenalty / 3600, 2
))
// --- Machine cost ---
const machine = selectMachine()
const machineName = variable('Machine', machine.name)
const machineCost = round(printTimeHours * machine.costPerHour, 2)
// --- Pricing ---
const hardwareCost = variable('Hardware cost', 0)
const getDiscount = createBands({ 10: 0.95, 50: 0.90, 100: 0.85, 250: 0.80 }, 1.0)
const baseCost = (materialCost + machineCost + hardwareCost) * getDiscount(quantity)
const MIN_PRICE = material.variables['minUnitPrice']
const unitPrice = variable('unitPrice', Math.max(MIN_PRICE, round(baseCost, 2)))
// --- Review gate ---
const largest = MACHINES[MACHINES.length - 1].bed
const oversized = length > largest.l || width > largest.w || height > largest.h
done(unitPrice, printTimeHours, oversized)Material variables: materialDensity, materialCostPerKg, baseSpeedNozzle, wallSpeedFactor, infillSpeedFactor, supportSpeedFactor, minUnitPrice
When to use this
All desktop and industrial FFF/FDM printers. Any extrusion process where print time is the main cost driver.
For very large parts (>500mm), use the Large Format FDM page instead.
Last updated on
Powder Bed - SLS / MJF
How to price powder bed processes (SLS, MJF, HSS, SAF) using shrink wrap volume, amortised setup costs, and quantity discounts.
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.