part.png

Ahhh the Multi Jet Fusion. What a machine, we love working with them at Phasio. It’s actually really interesting from a pricing perspective because HP offers some creative ways to adjust the business model that surrounds the machine. Let’s talk a little about that first.

HP created the concept of “3D as a Service” (3DaaS) with their MJF printers. Many manufacturers pay HP on a combined print height and space utilisation (packing density) pricing model. That’s akin to a “pay-per-use” model, which is great for flexibility but can make pricing complex.

Generally speaking, your nesting algorithm should be trying to minimize build height in the MJF machine as greater height means more material and hence a greater cost. This is intuitively a bit different from CNC machining, where you pay for the time it takes to cut the part, not the volume of material used. It’s also different from SLA or SLS where the cost is more about the resin or powder used rather than the build volume.

In this post, I’ll walk through a complete methodology for pricing HP Multi Jet Fusion parts that accounts for build volume optimization, packing density, and quantity economics. We’ll close with a working pricing algorithm you can implement directly.

The MJF Pricing Challenge

Before we dive in, let’s acknowledge the elephant in the room: MJF pricing is complicated. Unlike CNC machining where you’re typically making one part at a time, with MJF you’re filling an entire build volume. The cost per part depends heavily on how efficiently you can pack that build.

This creates some counterintuitive pricing dynamics. A small, simple part might be expensive per unit if it’s the only thing in the build. But that same part becomes incredibly cost-effective when you’re printing 500 of them and can pack the build volume efficiently.

The methodology I’m sharing here isn’t perfect—no pricing equation ever is. But it gives you a systematic way to quote MJF parts that accounts for the unique economics of powder bed fusion printing.

Understanding the HP MJF Build Volume

Let’s start with the basics. The HP Multi Jet Fusion 5200 has a build volume of 380 × 280 × 380mm. This is your playground, and the goal is to use as much of it as efficiently as possible.

const PRINTER_X = 380
const PRINTER_Y = 280  
const PRINTER_Z = 380

But here’s the thing—you can’t just pack parts edge-to-edge. You need spacing between parts for powder flow and part removal. This is where the model offset comes in:

const modelOffset = material.variables['modelOffset'] // typically 2-5mm

The Core Concept: Parts Per Build

The foundation of MJF pricing is figuring out how many parts you can fit in a single build. But it’s not as simple as just dividing volumes. Part orientation matters enormously.

Consider a part that’s 100mm × 20mm × 10mm. Depending on how you orient it, you might fit:

  • 3 parts (standing tall: 100mm height)
  • 19 parts (lying flat: 10mm height)
  • 12 parts (on its side: 20mm height)

Our algorithm tests all six possible orientations and picks the one that maximizes parts per build:

function calculatePartsPerBuild(): number {
  const orientations = [
    [width, height, length],
    [width, length, height], 
    [height, length, width],
    [height, width, length],
    [length, width, height],
    [length, height, width]
  ]
  
  let maxParts = 0
  
  for (const [w, h, l] of orientations) {
    const partsX = Math.floor(PRINTER_X / (w + modelOffset))
    const partsY = Math.floor(PRINTER_Y / (h + modelOffset))
    const partsZ = Math.floor(PRINTER_Z / (l + modelOffset))
    const totalParts = partsX * partsY * partsZ
    
    maxParts = Math.max(maxParts, totalParts)
  }
  
  return maxParts
}

Packing Density Reality Check

Here’s where things get interesting. Just because you can theoretically fit 1000 tiny parts in a build doesn’t mean you should. There’s a practical limit to packing density based on powder flow, heat management, and part quality.

We calculate the effective packing density and apply a maximum threshold:

function calculatePackingDensity(partsPerBuild: number, printerVolumeCm3: number): number {
  return (partsPerBuild * volume / 1000) / printerVolumeCm3
}

If the packing density exceeds your maximum (typically 10-15%), the algorithm reduces the parts per build accordingly. This prevents quality issues from over-packing.

Two Pricing Approaches

Once we know how many parts fit in a build, we calculate cost using two different methods and take the higher one:

Method 1: Model Parameters Cost

This is the “traditional” 3D printing approach—price based on the part’s physical properties:

function calculateCostBasedOnModelParameters(): number {
  // Material cost based on volume
  const volumeCost = (volume / 1000) * volumePrice
  
  // Processing cost based on surface area  
  const areaCost = (area / 100) * areaPrice
  
  // Penalty for inefficient bounding box usage
  const excessVolumeCost = ((specification.minBoundingBoxVolume - volume) / 1000) * 0.01
  
  return volumeCost + areaCost + excessVolumeCost
}

Method 2: Build Efficiency Cost

This approach divides the full build cost by the number of parts you can actually fit:

function calculateCostBasedOnBuild(maxPartsPerBuild: number): number {
  const packingEfficiency = (maxPartsPerBuild * volume) / (PRINTER_X * PRINTER_Y * PRINTER_Z * maxPackingDensity)
  return (packingEfficiency * priceForFullBuild) / maxPartsPerBuild
}

The build efficiency method is particularly important for larger parts or small quantities where you’re not utilizing the full build volume.

Quantity Discounting

Here’s where MJF pricing gets really interesting. Unlike traditional manufacturing where quantity discounts are often arbitrary, with MJF they’re tied directly to build efficiency.

The algorithm applies quantity discounts based on how close you get to filling an optimal build:

function calculateDiscount(maximumUnitPrice: number, minimumUnitPrice: number, maxPartsPerBuild: number): number {
  const fullDiscountQuantity = discountSpeed * maxPartsPerBuild
  
  if (quantity <= 1) {
    return maximumUnitPrice
  } else if (quantity >= fullDiscountQuantity) {
    return minimumUnitPrice  
  } else {
    // Linear interpolation between max and min price
    const progress = (quantity - 1) / (fullDiscountQuantity - 1)
    return maximumUnitPrice - (progress * (maximumUnitPrice - minimumUnitPrice))
  }
}

This creates a pricing curve where unit costs drop as quantities increase toward optimal build utilization.

Margin and Profit Floors

The final piece is ensuring you maintain healthy margins while staying competitive. The algorithm applies different margin percentages and profit floors based on volume:

  • High-margin, low-profit floor for small quantities
  • Low-margin, high-profit floor for large quantities

This prevents you from accidentally quoting below cost on complex parts or leaving money on the table with high-volume orders.

A Complete Example

Let’s walk through a real example. Imagine a customer wants 50 units of a part that’s 30mm × 20mm × 15mm with a volume of 5,000 mm³.

Step 1: Calculate parts per build

Testing all orientations, we find the best fit is 12 × 14 × 25 = 4,200 parts per build (in the 15mm height orientation).

Step 2: Check packing density

Packing density = (4,200 × 5,000) ÷ (380 × 280 × 380) = 55%

This exceeds our 12% maximum, so we reduce to: 4,200 ÷ (55% ÷ 12%) = 917 parts per build.

Step 3: Calculate base costs

  • Model parameters cost: $2.50 per part
  • Build efficiency cost: $180 (full build) ÷ 917 parts = $0.20 per part
  • Base cost = max($2.50, $0.20) = $2.50

Step 4: Apply quantity discount

With 50 parts ordered and 917 possible per build, we’re at 5.5% utilization. This places us in the high-price range of the discount curve.

Step 5: Final pricing

After applying margins and quantity adjustments: $3.85 per part.

The Complete Algorithm

Here’s the full MJF pricing equation you can implement directly in Phasio or adapt for your own use:

// MJF Pricing Algorithm

// Phasio-provided variables
const { material, width, height, length, volume, area } = specification
const { quantity } = requisition

// Constants
const PRINTER_X = 380
const PRINTER_Y = 280
const PRINTER_Z = 380

// Material variables
const modelOffset = material.variables['modelOffset']
const priceForFullBuild = material.variables['priceForFullBuild']
const volumePrice = material.variables['volumePrice']
const areaPrice = material.variables['areaPrice']
const minMargin = material.variables['minMargin']
const maxMargin = material.variables['maxMargin']
const minProfitPerPartAtMinMargin = material.variables['minProfitPerPartAtMinMargin']
const minProfitPerPartForMaxMargin = material.variables['minProfitPerPartForMaxMargin']

// Configurable variables
const discountSpeed = variable('Discount-Speed', material.variables['discountSpeed'])
const maxPackingDensity = variable('max-packing-density', material.variables['maxPackingDensity'])

function calculatePartsPerBuild(): number {
  const orientations = [
    [width, height, length],
    [width, length, height],
    [height, length, width],
    [height, width, length],
    [length, width, height],
    [length, height, width]
  ]

  let maxParts = 0

  for (const [w, h, l] of orientations) {
    const partsX = Math.floor(PRINTER_X / (w + modelOffset))
    const partsY = Math.floor(PRINTER_Y / (h + modelOffset))
    const partsZ = Math.floor(PRINTER_Z / (l + modelOffset))
    const totalParts = partsX * partsY * partsZ

    maxParts = Math.max(maxParts, totalParts)
  }

  return maxParts
}

function calculatePrinterVolume(): number {
  return (PRINTER_X * PRINTER_Y * PRINTER_Z) / 1000 // Convert to cm³
}

function calculatePackingDensity(partsPerBuild: number, printerVolumeCm3: number): number {
  return (partsPerBuild * volume / 1000) / printerVolumeCm3
}

function calculateMaxPartsPerBuild(partsPerBuild: number, effectivePackingDensity: number): number {
  if (effectivePackingDensity < maxPackingDensity) {
    return partsPerBuild
  } else {
    return Math.floor(partsPerBuild / (effectivePackingDensity / maxPackingDensity))
  }
}

function calculateCostBasedOnModelParameters(): number {
  const volumeCost = (volume / 1000) * volumePrice
  const areaCost = (area / 100) * areaPrice
  const excessVolumeCost = ((specification.minBoundingBoxVolume - volume) / 1000) * 0.01
  return volumeCost + areaCost + excessVolumeCost
}

function calculateCostBasedOnBuild(maxPartsPerBuild: number): number {
  const packingEfficiency = (maxPartsPerBuild * volume) / (PRINTER_X * PRINTER_Y * PRINTER_Z * maxPackingDensity)
  return (packingEfficiency * priceForFullBuild) / maxPartsPerBuild
}

function applyMarginAndProfitFloor(baseCost: number, marginPercent: number, minProfit: number): number {
  const marginAmount = baseCost * (marginPercent / 100)
  return baseCost + Math.max(minProfit, marginAmount)
}

function calculateUnitPrice() {
  const partsPerBuild = calculatePartsPerBuild()
  const printerVolumeCm3 = calculatePrinterVolume()
  const effectivePackingDensity = calculatePackingDensity(partsPerBuild, printerVolumeCm3)
  const maxPartsPerBuild = variable('Max-Parts-Per-Build', calculateMaxPartsPerBuild(partsPerBuild, effectivePackingDensity))

  const modelParametersCost = variable('Model-Parameters-Cost', calculateCostBasedOnModelParameters())
  const buildCost = variable('Build-Cost', calculateCostBasedOnBuild(maxPartsPerBuild))

  const baseCost = Math.max(modelParametersCost, buildCost)

  const maximumUnitPrice = applyMarginAndProfitFloor(baseCost, maxMargin, minProfitPerPartForMaxMargin)
  const minimumUnitPrice = applyMarginAndProfitFloor(baseCost, minMargin, minProfitPerPartAtMinMargin)

  return { maximumUnitPrice, minimumUnitPrice, maxPartsPerBuild }
}

function calculateDiscount(maximumUnitPrice: number, minimumUnitPrice: number, maxPartsPerBuild: number): number {
  const fullDiscountQuantity = discountSpeed * maxPartsPerBuild

  if (quantity <= 1) {
    return maximumUnitPrice
  } else if (quantity >= fullDiscountQuantity) {
    return minimumUnitPrice
  } else {
    const progress = (quantity - 1) / (fullDiscountQuantity - 1)
    return maximumUnitPrice - (progress * (maximumUnitPrice - minimumUnitPrice))
  }
}

// Calculate the final unit price
const { maximumUnitPrice, minimumUnitPrice, maxPartsPerBuild } = calculateUnitPrice()
const finalUnitPrice = variable('Unit-Price', calculateDiscount(maximumUnitPrice, minimumUnitPrice, maxPartsPerBuild))

done(finalUnitPrice)

Final Thoughts

MJF pricing will never be as straightforward as traditional manufacturing because you’re optimizing for build efficiency, not individual part complexity. But that’s also what makes it powerful—when you get the economics right, you can offer incredibly competitive pricing while maintaining healthy margins.

This algorithm gives you a starting point that accounts for the unique aspects of powder bed fusion: build volume constraints, packing density limits, and quantity-based efficiency gains.

As with any pricing model, you’ll need to calibrate the variables based on your actual costs and market position. But this framework should give you a solid foundation to build on.

Frequently Asked Questions About MJF Pricing

Why does quantity affect MJF pricing so much more than other manufacturing methods? Unlike CNC machining where you make parts one at a time, MJF fills an entire build chamber (380×280×380mm). A single part might use only 2% of the build volume, making it expensive per unit. But 100 of that same part could fill 80% of the build, dropping the unit cost by 10x. This is why MJF pricing curves are so steep compared to traditional manufacturing.

How does MJF pricing compare to SLA and injection molding? For volumes under 50 parts, SLA is often cheaper ($5-15 vs $8-25 for MJF). Above 100 parts, MJF typically wins on cost. Compared to injection molding, MJF is cost-effective up to about 10,000 parts, after which tooling costs make injection molding cheaper despite higher per-part material costs.

What makes an MJF part expensive to produce? The biggest cost drivers are poor build utilization and inefficient part orientation. Parts that force tall builds (high Z-axis usage) cost more due to HP’s per-layer pricing model. Complex internal geometries don’t significantly impact cost, but parts with large bounding boxes relative to their actual volume get penalized in most pricing algorithms.

Is there a recommended minimum order quantity or price for MJF parts? Most service bureaus set minimum order values ($100-500) rather than minimum quantities, since build economics make single parts uneconomical. At Phasio, we typically recommend quantities of 25+ for cost-effective MJF pricing, though we can quote smaller quantities when the geometry is suitable for efficient nesting.


What topics would you like me to cover in future posts? Feel free to reach out on LinkedIn with your suggestions!