3D printer skew calculator: XY skew factor for Marlin and Klipper

The skew factor is tan(90° − corner A), with corner A worked out from the two diagonals and one side of a printed test square. Type AC, BD and AD to get Marlin's M852 value, Klipper's SET_SKEW line, the skew angle and how far out of square a 100 mm part prints.

Test square

Print it with skew correction off. Corner A sits nearest the plane's origin, D along the first axis, B along the second and C opposite A.

mm
mm
mm

Along X for the XY and XZ planes, along Y for YZ. Leave out any flats on the corners.

Second pass (Marlin)

Only if this square was printed with a skew factor already active. Leave it at 0 otherwise.

M852 with no parameters reports it. The calculator adds the new factor to it, as Marlin's docs say.

Marlin skew factor

–

Klipper computes the same number from the same lengths.

Skew angle

–

Error over 100 mm

–

Side AB, worked out from the diagonals

–

Marlin at runtime (needs SKEW_CORRECTION_GCODE)

–

Marlin Configuration.h instead

–

Klipper console

–

Needs a [skew_correction] line in printer.cfg.

Klipper start G-code, after homing and any purge

–

Klipper CALC_MEASURED_SKEW shows

–

RepRapFirmware (opposite sign)

–

The formula

Print a square, label its corners A to D and measure the diagonals AC and BD and the side AD. Marlin and Klipper both turn those three lengths into a skew factor in two steps:

AB     = √(2 × AC² + 2 × BD² − 4 × AD²) / 2
factor = tan(90° − acos((AC² − AB² − AD²) / (2 × AB × AD)))
  • AC and BD are the diagonals. AD is the side along the first axis: X for the XY and XZ planes, Y for YZ.
  • AB is the side along the second axis, worked out from the other three with the parallelogram law.
  • The acos term is the angle at corner A, from the law of cosines. The factor is the tangent of how far that corner sits from 90°.

These are the lines in Marlin’s Conditionals-5-post.h (_GET_SIDE and _SKEW_FACTOR) and in Klipper’s calc_skew_factor(), which Klipper’s source calls a port of Marlin’s skew correction. The same three lengths give the same factor in both.

The calculator’s other results follow from the factor:

skew angle         = atan(factor)
error over 100 mm  = 100 × factor

When AC is longer than BD, corner A is under 90° and the factor is positive. On the XY plane both firmwares then plan every move with X reduced by Y × factor, which pulls the leaning side back into line. Marlin’s docs put it in numbers: a factor of 0.004 shifts X by 0.4 mm over 100 mm of Y.

How do you calculate skew correction for a 3D printer?

Print a test square with skew correction off, measure diagonals AC and BD and side AD, and work out side AB = √(2AC² + 2BD² − 4AD²) / 2. The factor is tan(90° − corner A). In Marlin send M852 I<factor> and M500. In Klipper send SET_SKEW XY=AC,BD,AD and save a profile.

  1. Turn correction off for the test print: M852 I0 in Marlin, SET_SKEW CLEAR=1 in Klipper (or leave [skew_correction] out of printer.cfg).
  2. Print the square with corner A toward the plane’s origin, as large as your calipers can measure.
  3. Measure AC, BD and AD two or three times each and average the readings.
  4. Type the averages into the calculator and copy the lines for your firmware.
  5. Print the same square in the same place with correction on and measure it again (see “Checking the correction” below).

Worked example: the square from Marlin’s docs

Marlin’s docs use AC = 141.70 mm, BD = 141.14 mm and AD = 100.00 mm. These are the calculator’s defaults.

  1. AB = √(2 × 141.70² + 2 × 141.14² − 4 × 100²) / 2 = √(40,157.78 + 39,841.00 − 40,000) / 2 = √39,998.78 / 2 = 99.998 mm.
  2. cos A = (141.70² − 99.998² − 100²) / (2 × 99.998 × 100) = 79.20 / 19,999.69 = 0.003960, so corner A is 89.773°.
  3. Factor = tan(90° − 89.773°) = tan(0.227°) = 0.003960. Marlin’s docs give “about 0.00396”.
  4. Error over 100 mm = 100 × 0.003960 = 0.40 mm. Corners B and C lean 0.40 mm toward +X for every 100 mm of Y.
  5. Marlin takes M852 I0.003960 then M500. Klipper takes SET_SKEW XY=141.7,141.14,100, and CALC_MEASURED_SKEW with the same lengths reports 0.003960 radians, 0.23 degrees.

Klipper prints the factor itself as “radians” and converts that to degrees. For skew this small, the result matches atan(factor) to two decimals.

Worked example: the square from Klipper’s docs

Klipper’s docs measure AC = 140.4 mm, BD = 142.8 mm and AD = 99.8 mm and enter them as SET_SKEW XY=140.4,142.8,99.8. AB works out to 100.459 mm and corner A to 90.971°. The factor is −0.016951: 0.97° of skew, with B and C leaning 1.70 mm toward −X per 100 mm of Y. Marlin would take M852 I-0.016951.

That much skew puts the diagonals of a 100 mm square 2.40 mm apart. Past a difference of about 1 mm, Marlin’s docs say to check the frame with a machinist’s square and re-square the gantry or Y rails, then let software correct what is left. Klipper’s docs give the same advice for a significantly skewed printer.

What is 3D printer skew?

Skew is when two of a printer’s axes are not at 90° to each other: X against Y, or Z against X or Y. Every square then prints as a parallelogram, and the error grows with the size of the part. A skew of 0.23° moves the back of a 100 mm square 0.40 mm sideways.

Prusa’s MK3 firmware measures XY skew during its XYZ calibration and sorts it into bands. Marlin’s docs add the point where the frame needs work before software correction.

Skew Error per 100 mm Diagonals of a 100 mm square differ by Published label
0.12° 0.21 mm 0.30 mm Prusa MK3: up to here, “X/Y axes are perpendicular”
0.25° 0.44 mm 0.62 mm Prusa MK3: up to here, “slightly skewed”; above it, “Severe skew”
0.41° 0.71 mm 1.00 mm Marlin docs: past about here, square the frame first

Prusa’s firmware notes the same bands in its own terms: 0.12° is 0.5 mm over 250 mm and 0.25° is 1.1 mm over 250 mm.

What a skewed print looks like, and what else looks like skew

What you see Likely cause Fix
A square with unequal diagonals; corners not 90° X and Y not perpendicular This calculator, XY plane
A wall or tower that leans steadily with height Z not square to X or Y; Marlin’s docs point to a leaning gantry or uprights Check the frame, then the XZ or YZ plane
A sudden sideways step at one layer A layer shift: belt tension or a loose pulley, per Prusa Belt tension calculator, pulley set screws
Every dimension off by the same percentage Wrong steps per mm Steps per mm calculator
Every dimension off by the same amount; small holes Backlash or over-extrusion Belts and pulleys first; holes with the hole compensation calculator

Marlin’s docs put skew after the mechanics, steps per mm and backlash, because backlash makes printed squares measure wrong. Re-measure skew after you change backlash compensation or steps per mm.

Printing and measuring the skew test

  • The test object. Marlin’s Configuration.h and Klipper’s docs both link the same square, thing:2563185 on Thingiverse. Marlin’s docs note that its clipped corners make the caliper jaws sit on the same points every time, and that any accurately modeled square works. Klipper’s docs also link an object with all three planes in one print.
  • Corner labels. Stand at the front and look down on the bed: X increases to the right and Y toward the back. A is the corner nearest X min and Y min. B sits up the Y side from A, D along the X side, and C opposite A.
  • XZ and YZ. Print the square standing up: X across and Z up for XZ, Y across and Z up for YZ. AD runs along X or Y and AB runs up Z.
  • Measuring AD. Leave out any flats the test object has on its corners, as Klipper’s docs say.
  • Size. A 0.1 mm misread on one diagonal of a 100 mm square moves the factor by about 0.0007, or 0.07 mm per 100 mm. On a 200 mm square the same misread costs half that, which is why Marlin’s docs say to print the square as large as your calipers can measure.

If the diagonals are equal within your measuring error, the axes are square and Marlin’s docs say you don’t need skew correction.

Setting skew correction in Marlin

Marlin needs these options in Configuration.h, in the Bed Skew Compensation section:

  • SKEW_CORRECTION enables the feature. It needs either the three lengths XY_DIAG_AC, XY_DIAG_BD and XY_SIDE_AD, or a factor in XY_SKEW_FACTOR. Without one of them, the firmware won’t compile.
  • SKEW_CORRECTION_GCODE adds M852 and loads the factor from EEPROM (the board’s saved-settings memory). Without it, the configured values are fixed.
  • SKEW_CORRECTION_FOR_Z adds the XZ and YZ planes. The stock file has no XZ_SIDE_AD, so Marlin uses XY_SIDE_AD for the XZ side unless you add one. The calculator’s Configuration.h output includes that define.

At runtime, M852 takes I for XY (S is the same), J for XZ and K for YZ. M500 saves the factor to EEPROM. For the Marlin docs’ square:

M852 I0.003960
M500

Or let the firmware compute the factor at build time from the lengths:

#define SKEW_CORRECTION
#define XY_DIAG_AC 141.7
#define XY_DIAG_BD 141.14
#define XY_SIDE_AD 100
#define SKEW_CORRECTION_GCODE
  • Marlin accepts factors from −1 to 1. A refused value with the reply min_skew_factor: -1 max_skew_factor: 1 usually means millimeters were typed instead of a factor.
  • After you flash new firmware, a factor saved in EEPROM wins over Configuration.h. Send M502 then M500 to take the configured one. M502 resets every setting to its firmware default, not only skew.
  • Skew isn’t applied within 1 mm of the minimum X or Y limit, or where the corrected point would fall outside the limits. Keep parts away from the edges.

Setting skew correction in Klipper

  1. Add a [skew_correction] section to printer.cfg. It takes no options.
  2. Send SET_SKEW XY=AC,BD,AD with your three lengths in that order. XZ= and YZ= work the same way, in any combination on one line.
  3. Send SKEW_PROFILE SAVE=my_skew_profile, then SAVE_CONFIG to write the profile to printer.cfg.
  4. Load the profile in your start G-code, after homing and after any purge line or nozzle wipe near the edge of the bed.
G28
; purge line or nozzle wipe here
SKEW_PROFILE LOAD=my_skew_profile

Klipper reads saved profiles at startup but starts every session with all three factors at zero. It applies a profile only on SKEW_PROFILE LOAD, so a start G-code without that line prints uncorrected. Klipper’s docs suggest SET_SKEW CLEAR=1 in the end G-code. They also warn that the correction can move the toolhead past the X or Y limits, so place parts away from the edges.

Checking the correction

Print the same square in the same place with correction on, and measure it again.

  • Marlin. The diagonals should now match within your measuring error. If a small difference remains, Marlin’s docs say to compute a factor from the new print and add it to the one already set. Type the new lengths, put the active factor in “Second pass”, and the calculator adds the two.
  • Klipper. Send CALC_MEASURED_SKEW AC=<ac> BD=<bd> AD=<ad> with the new lengths. Klipper’s docs say the result should be lower than what GET_CURRENT_SKEW reports. SET_SKEW expects lengths from a square printed with no correction, so for a fresh value send SET_SKEW CLEAR=1 and print the test again.
  • Worse than before. The sign is wrong, usually because the corners were labeled in the wrong order. Re-label with A at X min and Y min and B along +Y, or in Marlin set M852 I to the negative of the value.

Skew correction in RepRapFirmware (Duet)

RepRapFirmware uses the opposite sign. Duet’s docs compute the factor as −tan(90° − corner A), so it is negative when AC is longer than BD. M556 takes a length and the deviation over it, with X for the XY plane, Y for YZ and Z for ZX. For the Marlin docs’ square that is M556 S100 X-0.396, which the calculator’s last line gives with the sign already flipped.

Published · Figures last checked