Dev.to WebDev πŸ›  Dev πŸ‘ 0 πŸ“– 4 min read

How to price a 3D print when one in eight fails

Most 3D printing price quotes fail in the same place: the seller adds up filament and electricity, gets a number like $7.50, and then discovers over a month that failed prints quietly ate the margin. The failure cost is

Most 3D printing price quotes fail in the same place: the seller adds up filament and electricity, gets a number like $7.50, and then discovers over a month that failed prints quietly ate the margin. The failure cost is real, but it belongs in a specific place in the math, not smeared into the material rate.

This note walks through the cost model behind a small 3D printing cost calculator I built for the Voxel3D project, with the exact formulas and a worked example you can reproduce in a REPL. It is about the quoting step, not about modeling. If your input is still a picture rather than a mesh, start earlier in the pipeline with the 3d model from image guide.

1. Four separate cost lines, not one "price per gram"

A print job has four independent cost drivers, and collapsing them into a single per-gram rate is what makes quotes drift:

  • Material: sliced weight in grams times your per-kilogram filament price. Include supports, brim, raft and purge in the weight; measuring only the finished part understates material by 10-30% on support-heavy jobs.
  • Electricity: printer watts times hours times your tariff. On an FDM machine this is usually the smallest line, often under $0.50 for an overnight print.
  • Labor: hands-on minutes times your hourly rate. Slicing, bed prep, post-processing, packaging. This line is routinely forgotten and is often larger than material.
  • Machine time: hours on the printer times a per-hour machine rate. This is the line that covers wear, maintenance and eventual replacement.

Keeping them separate matters because they scale differently. Double the print time and electricity and machine time double, but labor does not. Double the model and material roughly doubles, but failure risk does not.

2. Failure is a divisor, not an add-on

If one print in eight fails, your failure rate is 12.5%. The tempting move is to add 12.5% to the cost. That is wrong, and the size of the error grows with the failure rate.

The correct model: to deliver N good prints at failure rate f, you start N / (1 - f) prints. So expected cost per delivered print is the base cost divided by (1 - f):

total = base / (1 - failure/100)

At 10% failure, dividing gives +11.1% over base; adding would give +10%. Close enough. At 30% failure, dividing gives +42.9%; adding gives +30%. That gap is your missing margin on exactly the jobs that fail most.

3. Markup multiplies the failure-adjusted cost

Markup applies after the failure adjustment, because a failed print consumes the same machine hours and material as a good one:

price = total * (1 + markup/100)

Markup on top of the raw base would systematically undercharge precisely when failure rates are high.

4. The implementation

The calculator keeps the model in one pure function so it can be unit-tested. This is the whole thing:

export type CostInput = {
  grams: number; perKg: number;       // material
  hours: number; watts: number; perKwh: number; // electricity
  minutes: number; perHour: number;   // labor
  machine: number;                    // per-hour machine rate
  failure: number; markup: number;    // percent
};

export function printingCost(v: CostInput) {
  if (Object.values(v).some(n => !Number.isFinite(n) || n < 0) || v.failure >= 100)
    return null;
  const material = v.grams / 1000 * v.perKg;
  const electricity = v.watts / 1000 * v.hours * v.perKwh;
  const labor = v.minutes / 60 * v.perHour;
  const machine = v.hours * v.machine;
  const base = material + electricity + labor + machine;
  const total = base / (1 - v.failure / 100);
  const price = total * (1 + v.markup / 100);
  if (!Number.isFinite(price)) return null;
  return { material, electricity, labor, machine, allowance: total - base, total, price };
}

Two guardrails worth copying: reject non-finite or negative inputs instead of letting NaN flow into a quote, and treat a 100% failure rate as "no answer" rather than dividing by zero.

5. A worked example

A customer wants a 320 g part, sliced at 16 hours. PLA at $22/kg, 150 W average draw, $0.20/kWh, 25 minutes of hands-on time at $15/h, machine rate $0.50/h, 12% failure rate, 25% markup:

material    320/1000 * 22          = $7.04
electricity 0.15 kW * 16 h * 0.20  = $0.48
labor       25/60 * 15             = $6.25
machine     16 * 0.50              = $8.00
base                               = $21.77
failure     21.77 / 0.88           = $24.74  (allowance $2.97)
price       24.74 * 1.25           = $30.92

Two things to notice. Labor plus machine time ($14.25) is twice the material cost; a per-gram quote would miss both. And the failure allowance is $2.97, not the $2.61 you would get from adding 12%.

6. Test the model, not just the UI

Because the function is pure, the quote math is one assertion away from a regression test. The project's test pins the default input so the displayed example cannot silently drift:

const c = printingCost(defaultCost)!;
expect(c.material).toBe(1);
expect(c.electricity).toBeCloseTo(0.12);
expect(c.total).toBeCloseTo(5.62 / 0.9);
expect(c.price).toBeCloseTo(5.62 / 0.9 * 1.2);

If someone later "simplifies" the failure handling to an additive term, this test fails loudly instead of quietly repricing every quote.

7. Where this fits

The calculator is one of four free tools around the model workflow. The others handle the geometry steps: converting a self-contained GLB into a printable mesh with the GLB to STL converter, turning a binary or ASCII STL into an inspectable Wavefront file with the STL to OBJ converter, and generating raised lettering as a binary STL with the text to STL tool.

None of the math here is exotic. The point is that failure belongs in the denominator, labor and machine time deserve their own lines, and the whole model fits in one tested function. Quote from that, and the end-of-month numbers stop surprising you.

πŸ“° Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.