I wanted a 3D print price calculator where you drop the sliced file and get a price, without uploading a 40 MB G-code file to anyone's server. It turned out to need about 30 lines of JavaScript. Here is how it works, and the slicer comment formats I had to handle.
Try it first if you like: 3D print price calculator (static page, nothing leaves the browser).
Only read the ends of the file
Slicers write their summary as comments, either in the header or in the footer. The middle is millions of moves you do not care about. Blob.slice() lets you read just the ends without loading the whole file into a string:
async function load(file) {
const head = await file.slice(0, 60000).text();
const tail = await file.slice(Math.max(0, file.size - 80000)).text();
const [grams, secs] = read(head + '\n' + tail);
}
That is 140 KB read no matter how big the file is, so it stays fast on a phone. file.text() would pull the whole file into one string first.
The comment formats
These are the lines I match, in order of preference:
| Slicer | Filament | Time |
|---|---|---|
| PrusaSlicer, OrcaSlicer | ; filament used [g] = 12.34 |
; estimated printing time (normal mode) = 1h 2m 3s |
| Bambu Studio | ; total filament weight [g] : 12.34 |
; total estimated time: 1h 2m 3s |
| Cura |
;Filament used: 4.12m (length only) |
;TIME:3723 (seconds) |
Multi-material files list one value per extruder, comma separated (= 5.10, 2.30), so sum them:
function read(text) {
let g = null, secs = null, mm = null, x;
if (x = text.match(/;\s*(?:total )?filament (?:used|weight) \[g\]\s*[=:]\s*([\d.,]+)/i))
g = x[1].split(',').filter(Boolean).reduce((a, b) => a + +b, 0);
if (x = text.match(/;\s*filament used \[mm\]\s*=\s*([\d.,]+)/i))
mm = x[1].split(',').filter(Boolean).reduce((a, b) => a + +b, 0);
if (x = text.match(/;\s*(?:total estimated time|estimated printing time[^=\n]*)\s*[=:]\s*([0-9dhms ]+)/i))
secs = hms(x[1]);
if (secs == null && (x = text.match(/;\s*TIME:\s*(\d+)/))) secs = +x[1];
if (g == null && mm == null && (x = text.match(/;\s*Filament used:\s*([\d.]+)\s*m/))) mm = +x[1] * 1000;
if (g == null && mm != null) g = Math.PI * 0.0875 ** 2 * (mm / 10) * 1.24; // 1.75 mm PLA
return [g, secs];
}
function hms(s) {
let t = 0;
for (const x of s.matchAll(/(\d+)\s*([dhms])/g)) t += +x[1] * { d: 86400, h: 3600, m: 60, s: 1 }[x[2]];
return t;
}
Two gotchas:
-
[^=\n]*in the time pattern matters. Without the\n, PrusaSlicer's "(normal mode)" and "(silent mode)" lines can run together and the match grabs the wrong one. - Cura only gives length. Grams = cross-section area x length x density. For 1.75 mm filament the radius is 0.0875 cm, length in cm, PLA about 1.24 g/cm³. PETG is 1.27, ABS 1.04, so the estimate is a little high for ABS.
From grams and hours to a price
Once you have grams and hours, the cost is mostly arithmetic people skip:
filament = grams / 1000 * spool price
power = watts * hours / 1000 * price per kWh
wear = printer cost / expected life hours * hours
buffer = (filament + power + wear) * failure %
your time = hands-on minutes / 60 * hourly rate
price = cost * (1 + markup) / (1 - selling fee %)
The last line is the one most calculators get wrong. If Etsy takes 9.5% of the sale, adding 9.5% to your price does not cover it; you divide by 0.905 instead. And hands-on time (removing supports, packing) is usually worth more than the filament.
Same idea, other tools
I built a few more single-file, no-backend calculators the same way this week, all phone-first with the numbers saved in localStorage:
- Resale price calculator: list price per platform (eBay, Poshmark with its flat fee under $15, Mercari, Depop, Facebook, Whatnot, Etsy) for a target profit.
- Gig driver real hourly: what a shift paid after gas, car wear and tax.
- Tax set-aside: how much of a freelance payment to save, with 2026 brackets and the next quarterly due date.
- Christmas gift budget splitter: a fair amount per person from one total.
If you sell prints and want the whole job log, spool tracking and printer payback in a spreadsheet, that is the 3D Print Shop Tracker. The parser above is free to copy.
Top comments (0)