Two HTML calculators and one CLI, built around a single admission: a 40 mm cube cannot resolve the shrinkage it is supposed to measure. The first tool answers “what do I put in my slicer” and answers 100 % more often than any other number. The second answers “how do I make it go in slowly” — which is not a shrinkage problem at all, but a taper.
“can you make it that it only calculates the shrinkage percentage” — then, a day later, “dont i need to only change the shrinkage”. The tool was asked twice to be smaller than it was, and the second question is the interesting one: it assumes shrinkage is the knob that fixes a fit. It isn't.
A shrinkage calculator that accepts several caliper readings per axis, uses the smallest
reading on each axis as the true size, averages those minima into one measured size, and then decides
whether the difference from nominal is real. If it isn't, the page refuses to hand over a scale factor:
the big tile reads 100.00 % and the arithmetic it declined to apply sits underneath it in
grey. That refusal is the feature. Every other shrink calculator on the internet multiplies your number
by a constant and smiles.
Slip / snug / glue / press, each a fixed gap per side, plus a live SVG cross-section. It answered “what clearance do I use” and produced a number that was wrong for the question actually being asked, because all four presets are the same clearance at the mouth and at the floor. A constant gap can only drop in or jam; neither is “goes in slowly”.
lock fit, and an insertion rampCalipers on a printed face are noisy, so one reading per axis became many, and the datum became
the minimum (a tilted caliper only ever reads big). A fifth fit — lock — made the
bite a linear ramp over the full depth instead of a constant interference, and a “How it goes in”
chart plots gap against insertion distance so the free travel and the bite zone are visible rather
than described. What was cut: the illusion that four presets covered the space.
Rather than stripping the fit tool, the shrink maths moved into its own 18 KB page: preset, nominal, three reading boxes, four numbers out, no diagrams. What was cut: pockets, lead-ins, ramps, warnings, and the entire notion that a shrink number implies a fit. One page, one job.
| Quantity | Formula | Why it matters |
|---|---|---|
| Shrink | (nom − meas) / nom × 100 | Signed on purpose: negative means the part came out bigger, which is not shrinkage and must not be “corrected” with a factor below 100. |
| Scale | nom / meas × 100 | Not 100 + shrink. They differ in the third decimal, and on a scaled pair the difference lands in the clearance. |
| Measured | mean(min per axis) | Minima per axis, then mean across axes. Averaging all readings instead pulls the datum up by every seam bead and every tilted jaw. |
| Noise floor | max(0.05, spread/2 + 0.02) | Your own data sets the bar the effect has to clear. 0.05 mm is the absolute floor for a hand-held caliper. |
| Pocket floor | max_reading + 2(clearance + bias) | Uses the largest reading, because the first-layer flare at the bottom of the cube is what enters the mouth first. |
| State | Trigger | Output |
|---|---|---|
| Preset | no readings | material constant, labelled “estimate only” |
| No usable shrink | |dev| ≤ noise floor | 100.00 %, raw value greyed beneath |
| Oversized | measured > nominal | refuses a sub-100 % scale; names flow / first layer as the real cause |
| Real shrink | dev > noise floor | shrink % + scale %, “apply to BOTH parts” |
| Only one axis | 1 of 3 filled | still calculates; coverage is a note, never the verdict |
| Junk / zero nominal | no parseable token / nom = 0 | falls back to preset / prints “—” — no NaN, no division by zero |
An outside measurement with a tilted caliper reads too big, never too small — the jaws bridge the part at an angle. So the minimum of several readings is the closest thing to the true flat-to-flat width, while the average is the true width plus a systematic bias. Holes invert: rock and keep the largest. The page implements the outside rule, and it is why a 0.090 mm spread doesn't move the answer as far as it would if the readings were averaged.
The threshold isn't a magic number: it is half the widest spread across the axes, plus 0.02 mm for caliper and handling slop, floored at 0.05 mm. Type three tight readings and the bar drops; type three sloppy ones and the tool demands more evidence before it will move the slicer. The measurement quality scores itself.
The lock fit ramps the gap from +0.16 mm/side at the mouth to
−0.04 mm/side at the floor across 20 mm. The cube is guided for 16 mm, then the last
4 mm tightens at 10 µm per mm of push. Same total interference as a press fit, spread
over a distance instead of an instant — which is the entire difference between “mallet” and
“firm push”.
If the taper stops before the floor, the parallel section below it becomes a hard stop and the part jams exactly where the bite ends. The tool warns when the bite zone falls under 1.5 mm (a jolt), when the bite exceeds 0.09 mm/side (walls splay or crack), and when the mouth is opened so wide that the first few millimetres simply fall in loose.
| Axis | Readings | Min | Spread | Shrink vs 40.00 |
|---|---|---|---|---|
| X | 1 reading, 40.000 | 40.000 | 0.000 | 0.000 % |
| Y | 3 readings, min 39.910 | 39.910 | 0.090 | 0.225 % |
| Z | 3 readings, min 40.030 | 40.030 | 0.030 | −0.075 % |
| measured | mean of minima = 39.980 mm | noise 0.065 | 0.050 % | |
The same cube also produced the fit numbers, and they come from a different datum on purpose: the pocket uses the largest reading (40.05 mm, the flared bottom), because that flare is the part that meets the mouth first. Pocket floor 39.97 mm, mouth 40.37 mm, taper 0.57°/side over 20 mm, corners R1.50 cube / R1.55 pocket.
A caliper reads to 0.01 mm. On a 40 mm witness that tick is 0.025 % — half of the effect being measured — and hand-held repeatability is nearer 0.05 mm (0.125 %). No amount of averaging inside one cube escapes that: the instrument, not the sample size, is the limit.
| Witness length | 1 caliper tick resolves | PLA 0.3 % appears as |
|---|---|---|
| 40 mm (this cube) | 0.025 % | 0.12 mm — 12 ticks |
| 100 mm bar | 0.010 % | 0.30 mm — 30 ticks |
| 200 mm bar | 0.005 % | 0.60 mm — 60 ticks |
This is why the page carries a material preset table (PLA 0.30 %, silk 0.35 %, CF 0.20 %, PETG 0.35 %, ABS 0.70 %) instead of pretending a cube produced a constant. The presets are literature averages: the tool tells you which way your part moved, the table tells you what the polymer usually does.
The question that started the second page: “don't I only need to change the shrinkage?” A scale factor multiplies everything, gaps included. Scale both the cube and the pocket to 100.3 % and the 0.16 mm mouth gap becomes 0.1605 mm — a 0.5 µm change in a fit that is measured in tenths of a millimetre. Correction preserves the fit by construction; that is what makes it a correction.
| Knob | Controls | Move it when |
|---|---|---|
| Slicer scale | overall size accuracy | a long witness proves the spool shrinks |
| Pocket mouth / floor | how it feels going in | always — this is the entry |
| Taper length | how gradual the bite is | the push is too sudden |
| Clearance | bite force at the seat | it holds too hard / not at all |
| Fit | Gap at mouth | Gap at floor | Behaviour |
|---|---|---|---|
| slip | +0.35 | +0.35 | thumb pressure, never binds |
| snug | +0.25 | +0.25 | high spots touch, jams halfway |
| glue | +0.20 | +0.20 | rattles; needs adhesive |
| press | −0.05 | −0.05 | interference arrives all at once — mallet |
| lock | +0.16 | −0.04 | guided, then progressive bite — firm push |
Gaps in mm/side, including the 0.10 mm hole bias on the first four. Only the last row has a different gap at each end; the rest are constant, which is exactly why they can only drop in or jam.
| Symptom | Cause | Handling |
|---|---|---|
| NaN on screen | text pasted into a number box | tokenizer rejects it; the token is dropped, not zeroed |
| 0.000 % with junk input | nothing parseable | falls back to the material preset, labelled as an estimate |
| “— %” | nominal blank or 0 | guard before the division; no Infinity, no crash |
| Verdict ignores a real shrink | coverage warning promoted to verdict | found in testing: the one-axis warning was masking the oversized branch. Coverage is now a note. |
| Tile contradicts the verdict | tile printed the raw scale | found in testing: tile read 100.05 % beside “leave it at 100 %”. Now prints 100.00 % with the raw value greyed. |
| 100.000 % beside a nonzero shrink | reachable only when “raw value is noise” | intended: the tile is the instruction, the sub-line is the evidence |
| Caption says “0.9 mm” for a 0.3 mm break | hard-coded string in the fit page | derived from the mouth gap, clamped 0.3–0.6 mm |
| Check | Result |
|---|---|
| Shrink page size | 18,556 bytes · 379 lines · 5,758 B CSS + 8,269 B JS |
| Shrink page sha256 | 2d52f897…ca490b3a (full: 2d52f8971f43e0c51fb46f363fb5be34c287208ef4d75886f0900cffca490b3a) |
| Mirror | byte-identical, same 18,556 bytes, same sha256 |
| External requests | 0 — no src, href, font, CDN or fetch |
| Fit page | 48,434 bytes · 832 lines |
| CLI | 13,991 bytes · 269 lines · shrink / fit / shrinklist |
| Maths checks | 9 / 9 — every displayed number re-derived in Python and compared to the DOM |
| UI states | 8 / 8 — empty, measured, real shrink, oversized, one axis, junk, zero nominal, after reset |
| Widths | 390 / 768 / 1440 — no horizontal overflow, no clipped text, min tap target 44 px |
| Console errors | 0 at every width |
| Served copy | differs from local by the site's skin injection (+~390 B) and Cloudflare's beacon (+~367 B); body is byte-identical after stripping both |