diff --git a/tracker/agb-tracker-interop/src/lib.rs b/tracker/agb-tracker-interop/src/lib.rs
index eee21c00..ad8a9b66 100644
--- a/tracker/agb-tracker-interop/src/lib.rs
+++ b/tracker/agb-tracker-interop/src/lib.rs
@@ -94,6 +94,7 @@ pub enum PatternEffect {
     PitchBend(Num<u32, 8>),
     Jump(Jump),
     SampleOffset(u16),
+    Retrigger(u8),
 }
 
 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
@@ -403,6 +404,7 @@ impl quote::ToTokens for PatternEffect {
                 quote! { Jump(#jump) }
             }
             PatternEffect::SampleOffset(offset) => quote! { SampleOffset(#offset) },
+            PatternEffect::Retrigger(ticks) => quote! { Retrigger(#ticks) },
         };
 
         tokens.append_all(quote! {
diff --git a/tracker/agb-tracker/src/lib.rs b/tracker/agb-tracker/src/lib.rs
index 527766bf..500f067f 100644
--- a/tracker/agb-tracker/src/lib.rs
+++ b/tracker/agb-tracker/src/lib.rs
@@ -613,6 +613,11 @@ impl TrackerChannel {
                     self.current_pos = Some(*offset);
                 }
             }
+            PatternEffect::Retrigger(ticks) => {
+                if tick % *ticks as u32 == 0 {
+                    self.current_pos = Some(0);
+                }
+            }
         }
     }
 
diff --git a/tracker/agb-xm-core/src/lib.rs b/tracker/agb-xm-core/src/lib.rs
index 018aa540..c96063ba 100644
--- a/tracker/agb-xm-core/src/lib.rs
+++ b/tracker/agb-xm-core/src/lib.rs
@@ -439,6 +439,17 @@ pub fn parse_module(module: &Module) -> agb_tracker_interop::Track {
                             PatternEffect::GlobalVolumeSlide(Num::new(first as i32) / 0x40)
                         }
                     }
+                    // R
+                    0x1B => {
+                        let first = effect_parameter >> 4;
+                        let second = effect_parameter & 0xF;
+
+                        if first != 0 {
+                            eprintln!("Unsupported retrigger effect volume {first}");
+                        }
+
+                        PatternEffect::Retrigger(second)
+                    }
                     e => {
                         let effect_char = char::from_digit(e as u32, 36)
                             .unwrap_or('?')
diff --git a/tracker/desktop-player/tests/retrigger.xm b/tracker/desktop-player/tests/retrigger.xm
new file mode 100644
index 00000000..e44bfbe0
Binary files /dev/null and b/tracker/desktop-player/tests/retrigger.xm differ