diff --git a/Marlin/src/gcode/control/M3-M5.cpp b/Marlin/src/gcode/control/M3-M5.cpp
index 213d17450a079a3b1e41d806be32fe20962debf4..52dbd575c95afecc8c3a83b4b9ba9fbedd70d270 100644
--- a/Marlin/src/gcode/control/M3-M5.cpp
+++ b/Marlin/src/gcode/control/M3-M5.cpp
@@ -27,6 +27,8 @@
 #include "../gcode.h"
 #include "../../module/stepper.h"
 
+uint8_t spindle_laser_power; // = 0
+
 /**
  * M3: Spindle Clockwise
  * M4: Spindle Counter-clockwise
@@ -59,10 +61,10 @@
  */
 
 // Wait for spindle to come up to speed
-inline void delay_for_power_up() { gcode.dwell(SPINDLE_LASER_POWERUP_DELAY); }
+inline void delay_for_power_up() { safe_delay(SPINDLE_LASER_POWERUP_DELAY); }
 
 // Wait for spindle to stop turning
-inline void delay_for_power_down() { gcode.dwell(SPINDLE_LASER_POWERDOWN_DELAY); }
+inline void delay_for_power_down() { safe_delay(SPINDLE_LASER_POWERDOWN_DELAY); }
 
 /**
  * ocr_val_mode() is used for debugging and to get the points needed to compute the RPM vs ocr_val line
@@ -70,26 +72,72 @@ inline void delay_for_power_down() { gcode.dwell(SPINDLE_LASER_POWERDOWN_DELAY);
  * it accepts inputs of 0-255
  */
 
-inline void ocr_val_mode() {
-  uint8_t spindle_laser_power = parser.value_byte();
+inline void set_spindle_laser_ocr(const uint8_t ocr) {
   WRITE(SPINDLE_LASER_ENABLE_PIN, SPINDLE_LASER_ENABLE_INVERT); // turn spindle on (active low)
-  if (SPINDLE_LASER_PWM_INVERT) spindle_laser_power = 255 - spindle_laser_power;
-  analogWrite(SPINDLE_LASER_PWM_PIN, spindle_laser_power);
+  analogWrite(SPINDLE_LASER_PWM_PIN, (SPINDLE_LASER_PWM_INVERT) ? 255 - ocr : ocr);
 }
 
-void GcodeSuite::M3_M4(bool is_M3) {
+#if ENABLED(SPINDLE_LASER_PWM)
 
-  planner.synchronize();   // wait until previous movement commands (G0/G0/G2/G3) have completed before playing with the spindle
-  #if SPINDLE_DIR_CHANGE
-    const bool rotation_dir = (is_M3 != SPINDLE_INVERT_DIR);
-    if (SPINDLE_STOP_ON_DIR_CHANGE \
-       && READ(SPINDLE_LASER_ENABLE_PIN) == SPINDLE_LASER_ENABLE_INVERT \
-       && READ(SPINDLE_DIR_PIN) != rotation_dir
-    ) {
-      WRITE(SPINDLE_LASER_ENABLE_PIN, !SPINDLE_LASER_ENABLE_INVERT);  // turn spindle off
+  void update_spindle_laser_power() {
+    if (spindle_laser_power == 0) {
+      WRITE(SPINDLE_LASER_ENABLE_PIN, !SPINDLE_LASER_ENABLE_INVERT);                      // turn spindle off (active low)
+      analogWrite(SPINDLE_LASER_PWM_PIN, SPINDLE_LASER_PWM_INVERT ? 255 : 0);             // only write low byte
+      delay_for_power_down();
+    }
+    else {                                                                                // Convert RPM to PWM duty cycle
+      constexpr float inv_slope = 1.0f / (SPEED_POWER_SLOPE),
+                      min_ocr = (SPEED_POWER_MIN - (SPEED_POWER_INTERCEPT)) * inv_slope,  // Minimum allowed
+                      max_ocr = (SPEED_POWER_MAX - (SPEED_POWER_INTERCEPT)) * inv_slope;  // Maximum allowed
+      int16_t ocr_val;
+           if (spindle_laser_power <= SPEED_POWER_MIN) ocr_val = min_ocr;                 // Use minimum if set below
+      else if (spindle_laser_power >= SPEED_POWER_MAX) ocr_val = max_ocr;                 // Use maximum if set above
+      else ocr_val = (spindle_laser_power - (SPEED_POWER_INTERCEPT)) * inv_slope;         // Use calculated OCR value
+      set_spindle_laser_ocr(ocr_val & 0xFF);                                              // ...limited to Atmel PWM max
+      delay_for_power_up();
+    }
+  }
+
+#endif // SPINDLE_LASER_PWM
+
+bool spindle_laser_enabled() {
+  return !!spindle_laser_power; // READ(SPINDLE_LASER_ENABLE_PIN) == SPINDLE_LASER_ENABLE_INVERT;
+}
+
+void set_spindle_laser_enabled(const bool enable) {
+  // Enabled by PWM setting elsewhere
+  spindle_laser_power = enable ? 255 : 0;
+  #if ENABLED(SPINDLE_LASER_PWM)
+    update_spindle_laser_power();
+  #else
+    if (enable) {
+      WRITE(SPINDLE_LASER_ENABLE_PIN, SPINDLE_LASER_ENABLE_INVERT);
+      delay_for_power_up();
+    }
+    else {
+      WRITE(SPINDLE_LASER_ENABLE_PIN, !SPINDLE_LASER_ENABLE_INVERT);
       delay_for_power_down();
     }
-    WRITE(SPINDLE_DIR_PIN, rotation_dir);
+  #endif
+}
+
+#if SPINDLE_DIR_CHANGE
+
+  void set_spindle_direction(const bool reverse_dir) {
+    const bool dir_state = (reverse_dir == SPINDLE_INVERT_DIR); // Forward (M3) HIGH when not inverted
+    if (SPINDLE_STOP_ON_DIR_CHANGE && spindle_laser_enabled() && READ(SPINDLE_DIR_PIN) != dir_state)
+      set_spindle_laser_enabled(false);
+    WRITE(SPINDLE_DIR_PIN, dir_state);
+  }
+
+#endif
+
+void GcodeSuite::M3_M4(const bool is_M4) {
+
+  planner.synchronize();   // wait until previous movement commands (G0/G0/G2/G3) have completed before playing with the spindle
+
+  #if SPINDLE_DIR_CHANGE
+    set_spindle_direction(is_M4);
   #endif
 
   /**
@@ -98,30 +146,16 @@ void GcodeSuite::M3_M4(bool is_M3) {
    * Then needed to AND the uint16_t result with 0x00FF to make sure we only wrote the byte of interest.
    */
   #if ENABLED(SPINDLE_LASER_PWM)
-    if (parser.seen('O')) ocr_val_mode();
+    if (parser.seen('O')) {
+      spindle_laser_power = parser.value_byte();
+      set_spindle_laser_ocr(spindle_laser_power);
+    }
     else {
-      const float spindle_laser_power = parser.floatval('S');
-      if (spindle_laser_power == 0) {
-        WRITE(SPINDLE_LASER_ENABLE_PIN, !SPINDLE_LASER_ENABLE_INVERT);                                    // turn spindle off (active low)
-        analogWrite(SPINDLE_LASER_PWM_PIN, SPINDLE_LASER_PWM_INVERT ? 255 : 0);                           // only write low byte
-        delay_for_power_down();
-      }
-      else {
-        int16_t ocr_val = (spindle_laser_power - (SPEED_POWER_INTERCEPT)) * (1.0f / (SPEED_POWER_SLOPE));  // convert RPM to PWM duty cycle
-        NOMORE(ocr_val, 255);                                                                             // limit to max the Atmel PWM will support
-        if (spindle_laser_power <= SPEED_POWER_MIN)
-          ocr_val = (SPEED_POWER_MIN - (SPEED_POWER_INTERCEPT)) * (1.0f / (SPEED_POWER_SLOPE));            // minimum setting
-        if (spindle_laser_power >= SPEED_POWER_MAX)
-          ocr_val = (SPEED_POWER_MAX - (SPEED_POWER_INTERCEPT)) * (1.0f / (SPEED_POWER_SLOPE));            // limit to max RPM
-        if (SPINDLE_LASER_PWM_INVERT) ocr_val = 255 - ocr_val;
-        WRITE(SPINDLE_LASER_ENABLE_PIN, SPINDLE_LASER_ENABLE_INVERT);                                     // turn spindle on (active low)
-        analogWrite(SPINDLE_LASER_PWM_PIN, ocr_val & 0xFF);                                               // only write low byte
-        delay_for_power_up();
-      }
+      spindle_laser_power = parser.intval('S', 255);
+      update_spindle_laser_power();
     }
   #else
-    WRITE(SPINDLE_LASER_ENABLE_PIN, SPINDLE_LASER_ENABLE_INVERT); // turn spindle on (active low) if spindle speed option not enabled
-    delay_for_power_up();
+    set_spindle_laser_enabled(true);
   #endif
 }
 
@@ -130,11 +164,7 @@ void GcodeSuite::M3_M4(bool is_M3) {
  */
 void GcodeSuite::M5() {
   planner.synchronize();
-  WRITE(SPINDLE_LASER_ENABLE_PIN, !SPINDLE_LASER_ENABLE_INVERT);
-  #if ENABLED(SPINDLE_LASER_PWM)
-    analogWrite(SPINDLE_LASER_PWM_PIN, SPINDLE_LASER_PWM_INVERT ? 255 : 0);
-  #endif
-  delay_for_power_down();
+  set_spindle_laser_enabled(false);
 }
 
 #endif // SPINDLE_LASER_ENABLE
diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp
index a25fc55c03c41dba49b98fae506c9633f85f2e12..900d6d0075251803193ff632f3d19cded4193d04 100644
--- a/Marlin/src/gcode/gcode.cpp
+++ b/Marlin/src/gcode/gcode.cpp
@@ -285,8 +285,8 @@ void GcodeSuite::process_parsed_command(
       #endif
 
       #if ENABLED(SPINDLE_LASER_ENABLE)
-        case 3: M3_M4(true ); break;                              // M3: turn spindle/laser on, set laser/spindle power/speed, set rotation direction CW
-        case 4: M3_M4(false); break;                              // M4: turn spindle/laser on, set laser/spindle power/speed, set rotation direction CCW
+        case 3: M3_M4(false); break;                              // M3: turn spindle/laser on, set laser/spindle power/speed, set rotation direction CW
+        case 4: M3_M4(true ); break;                              // M4: turn spindle/laser on, set laser/spindle power/speed, set rotation direction CCW
         case 5: M5(); break;                                      // M5 - turn spindle/laser off
       #endif
 
diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h
index 4a34ed6377ef0a7a5da7b3f08f9f5b37062794d7..6aeeb35b2ae3062e2aff449f70a0b0cafcb29d1f 100644
--- a/Marlin/src/gcode/gcode.h
+++ b/Marlin/src/gcode/gcode.h
@@ -437,7 +437,7 @@ private:
   #endif
 
   #if ENABLED(SPINDLE_LASER_ENABLE)
-    static void M3_M4(bool is_M3);
+    static void M3_M4(const bool is_M4);
     static void M5();
   #endif
 
diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h
index 2878747b3df66dd99b298d91ae82561a3e168ce3..646815883422c255e1907f2500569b324dadeb96 100644
--- a/Marlin/src/lcd/language/language_en.h
+++ b/Marlin/src/lcd/language/language_en.h
@@ -169,6 +169,21 @@
 #ifndef MSG_COOLDOWN
   #define MSG_COOLDOWN                        _UxGT("Cooldown")
 #endif
+#ifndef MSG_LASER
+  #define MSG_LASER                           _UxGT("Laser")
+#endif
+#ifndef MSG_LASER_OFF
+  #define MSG_LASER_OFF                       MSG_LASER _UxGT(" Off")
+#endif
+#ifndef MSG_LASER_ON
+  #define MSG_LASER_ON                        MSG_LASER _UxGT(" On")
+#endif
+#ifndef MSG_LASER_POWER
+  #define MSG_LASER_POWER                     MSG_LASER _UxGT(" power")
+#endif
+#ifndef MSG_SPINDLE_REVERSE
+  #define MSG_SPINDLE_REVERSE                 _UxGT("Spindle Reverse")
+#endif
 #ifndef MSG_SWITCH_PS_ON
   #define MSG_SWITCH_PS_ON                    _UxGT("Switch power on")
 #endif
diff --git a/Marlin/src/lcd/menu/menu_temperature.cpp b/Marlin/src/lcd/menu/menu_temperature.cpp
index be2642df040b6ab3cf1afc1a3c945750c8675b13..15fb6be1d5b394b4cc319d72a81b1cd1cb30ddb5 100644
--- a/Marlin/src/lcd/menu/menu_temperature.cpp
+++ b/Marlin/src/lcd/menu/menu_temperature.cpp
@@ -303,6 +303,47 @@ void _lcd_preheat(const int16_t endnum, const int16_t temph, const int16_t tempb
 
 #endif // HAS_TEMP_HOTEND || HAS_HEATED_BED
 
+#if ENABLED(SPINDLE_LASER_ENABLE)
+
+  extern uint8_t spindle_laser_power;
+  bool spindle_laser_enabled();
+  void set_spindle_laser_enabled(const bool enabled);
+  #if ENABLED(SPINDLE_LASER_PWM)
+    void update_spindle_laser_power();
+  #endif
+
+  inline void _lcd_spindle_laser_off() { set_spindle_laser_enabled(false); }
+  inline void _lcd_spindle_laser_on(const bool is_M4) {
+    #if SPINDLE_DIR_CHANGE
+      set_spindle_direction(is_M4);
+    #endif
+    set_spindle_laser_enabled(true);
+  }
+  inline void _lcd_spindle_laser_on() { _lcd_spindle_laser_on(false); }
+  #if SPINDLE_DIR_CHANGE
+    inline void _lcd_spindle_on_reverse() { _lcd_spindle_laser_on(true); }
+  #endif
+
+  void menu_spindle_laser() {
+    START_MENU();
+    MENU_BACK(MSG_MAIN);
+    if (spindle_laser_enabled()) {
+      #if ENABLED(SPINDLE_LASER_PWM)
+        MENU_ITEM_EDIT_CALLBACK(int3, MSG_LASER_POWER, &spindle_laser_power, SPEED_POWER_MIN, SPEED_POWER_MAX, update_spindle_laser_power);
+      #endif
+      MENU_ITEM(function, MSG_LASER_OFF, _lcd_spindle_laser_off);
+    }
+    else {
+      MENU_ITEM(function, MSG_LASER_ON, _lcd_spindle_laser_on);
+      #if SPINDLE_DIR_CHANGE
+        MENU_ITEM(function, MSG_SPINDLE_REVERSE, _lcd_spindle_on_reverse);
+      #endif
+    }
+    END_MENU();
+  }
+
+#endif // SPINDLE_LASER_ENABLE
+
 void menu_temperature() {
   START_MENU();
   MENU_BACK(MSG_MAIN);
@@ -391,6 +432,10 @@ void menu_temperature() {
 
   #endif // HAS_TEMP_HOTEND
 
+  #if ENABLED(SPINDLE_LASER_ENABLE)
+    MENU_ITEM(submenu, MSG_LASER_MENU, menu_spindle_laser);
+  #endif
+
   END_MENU();
 }