Skip to content

Instantly share code, notes, and snippets.

@Pyr-000
Created January 27, 2026 17:38
Show Gist options
  • Select an option

  • Save Pyr-000/d034df049015c16a7b407fc4db8c85b4 to your computer and use it in GitHub Desktop.

Select an option

Save Pyr-000/d034df049015c16a7b407fc4db8c85b4 to your computer and use it in GitHub Desktop.
Support interface fan speed - GCode post processing script

A GCode post processing script to apply a fan speed override by feature type

While this script is intended for use with PrusaSlicer, it should also work on any related/similar slicers.

The fan speed overrides are applied based on feature type comments (e.g. ";TYPE:Support material interface"), any slicer which creates these comments should be compatible with this script.

From what I can tell, PrusaSlicer creates these comments even with "verbose G-code" set to off. Turning it on should not be required (optional).


Functionality

  • By default, this script is configured to ensure that the fan speed is set to 100% (255.0) during support material interface features. Once a feature with a speed override ends, fan speed should return to the last previously requested fan speed.
  • The script can be edited to add a fan speed override for any feature type with a unique gcode comment (in OVERRIDES, line 19).
  • Available feature types should include: 'Support material', 'Perimeter', 'Custom', 'Top solid infill', 'Internal infill', 'Support material interface', 'Bridge infill', 'External perimeter', 'Solid infill', 'Overhang perimeter', 'Skirt/Brim'

Usage

Requires python3 to be installed.

For linux, adding the script path to Post-processing scripts in print settings > output options and updating the first line of the script to point to the correct python3 binary (if needed) should suffice.

For windows, the entry in print settings > output options should be:

X:\path\to\python3\python.exe X:\path\to\script\postprocess_gcode_maxFanForSupports.py;

#! python3
from sys import argv
from pathlib import Path
import re
from time import sleep
try:
from tqdm import tqdm
from tqdm import write
except ImportError:
tqdm = None
write = print
# clean up some excess comments. set to False if you want to chain other scripts which rely on comments
# removes additional in-line feature comments after G1 commands as well as ";WIDTH:" comments
REDUCE_FILESIZE = False
# Set your fan speed overrides for any feature here!
# types: ['Support material', 'Perimeter', 'Custom', 'Top solid infill', 'Internal infill', 'Support material interface', 'Bridge infill', 'External perimeter', 'Solid infill', 'Overhang perimeter', 'Skirt/Brim']
OVERRIDES = {
"Support material interface": 255,
#"Support material": 255, # If this (mostly) freezes the material fast enough, it may reduce curling... If not, cooling boost on supports probably makes curling worse...
}
# TODO: Add min/max range overrides which clamp values to between these values during a feature? Per-feature fan speed multiplier?
# Common type comments:
# {';TYPE:Skirt/Brim', ';TYPE:External perimeter', ';TYPE:Top solid infill', ';TYPE:Overhang perimeter', ';TYPE:Internal infill', ';TYPE:Support material', ';TYPE:Support material interface', ';TYPE:Bridge infill', ';TYPE:Perimeter', ';TYPE:Custom', ';TYPE:Solid infill'}
# for a fan speed command (M106), return the requested speed value
def getSpeed(line):
try:
return float(re.search("S\\d+\\.?\\d*",line).group(0).replace("S",""))
except Exception as e:
write(f"failed to parse speed from line '{line}' - {e}")
return 255.0 # no match will also yield an exception (M106 without any S parameter sets full speed)
# for a given fan speed, yield the correct gcode command (M107 if speed 0, M106 with speed otherwise). Add a comment to the line.
def getFanCommand(speed:float, marker:str="OVERRIDE"):
return "M107 ; {marker}: fan off" if (not isinstance(speed, (float,int))) or speed <= 0 else f"M106 S{speed:.1f} ; {marker}: set speed"
# get the gcode file to process
in_file = Path(argv[1])
assert in_file.is_file() and in_file.exists(), f"Input arg '{argv[1]}' does not resolve to a valid file!"
set_speed = 0.0
out_lines = []
override_fan = False
overridden_command_count = 0
overridden_feature_count = 0
fan_commands_kept_count = 0
for line in in_file.read_text().splitlines() if tqdm is None else tqdm(in_file.read_text().splitlines()):
# process line input
line = line.strip()
# process feature type changes
if line.startswith(";TYPE:"):
out_lines.append(line) # pass the type comment to the output
feature_type = line[len(";TYPE:"):]
if feature_type in OVERRIDES: # if this type has an override, enter override mode and emit the requested override fan speed command (M106)
override_fan = True
out_lines.append(getFanCommand(OVERRIDES[feature_type], "START OVERRIDE"))
overridden_feature_count += 1
elif override_fan: # when exiting from an override type, return to the last requested fan speed from the original gcode. Set override mode off.
out_lines.append(getFanCommand(set_speed, "END OVERRIDE"))
override_fan = False
# process fan commands
elif line.startswith("M106"):
set_speed = getSpeed(line) # store the last fan speed requested by the original gcode
if not override_fan: # do not emit fan speed changes while fan override is active
out_lines.append(line)
fan_commands_kept_count += 1
else:
overridden_command_count += 1
if not REDUCE_FILESIZE:
out_lines.append(";{line}")
elif line.startswith("M107"): # interpret fan off command as fan speed 0
set_speed = 0
if not override_fan: # do not emit fan speed changes while fan override is active
out_lines.append(line)
fan_commands_kept_count += 1
else:
overridden_command_count += 1
if not REDUCE_FILESIZE:
out_lines.append(";{line}")
elif line.startswith("G1") and REDUCE_FILESIZE: # most lines are G1 commands. remove verbose comments, save some space
out_lines.append(line.split(";")[0].strip())
elif line.startswith(";WIDTH:") and REDUCE_FILESIZE:
pass # some comments must be kept (e.g. thumbnail) but there can be a lot of these which we won't need
else: # if the line does not match any pattern we want to change, pass it on as-is.
out_lines.append(line)
# if we end the gcode in an override state, finally return to the last requested fan speed, ususally off.
if override_fan:
out_lines.append("M107 ; END OVERRIDE: return to fan off" if set_speed <= 0 else f"M106 S{set_speed:.1f} ; END OVERRIDE: return to requested speed")
# overwrite the file with our new lines
in_file.write_text("\n".join(out_lines))
# some metrics to observe the fact that something has happened (for the one second they are visible)
print(f"done! {overridden_command_count} fan speed commands removed, for {overridden_feature_count} local feature overrides. {fan_commands_kept_count} fan commands unchanged.")
sleep(1.0) # wait a second to display the "done" message in the terminal. Optional.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment