diff --git a/src/action.rs b/src/action.rs
index 911cc26..344898b 100644
--- a/src/action.rs
+++ b/src/action.rs
@@ -1,10 +1,32 @@
-//! The different actions that can be done.
+//! The different actions that can be executed via any given key.
 
 use crate::key_code::KeyCode;
 use crate::layout::{StackedIter, WaitingAction};
 use core::fmt::Debug;
 
-/// Behavior configuration of HoldTap.
+/// The different types of actions we support for key sequences/macros
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub enum SequenceEvent {
+    /// No operation action: just do nothing (a placeholder).
+    NoOp,
+    /// A keypress/keydown
+    Press(KeyCode),
+    /// Key release/keyup
+    Release(KeyCode),
+    /// A shortcut for `Press(KeyCode), Release(KeyCode)`
+    Tap(KeyCode),
+    /// For sequences that need to wait a bit before continuing
+    Delay {
+        /// How long (in ticks) this Delay will last
+        duration: u32, // NOTE: This isn't a u16 because that's only max ~65 seconds (assuming 1000 ticks/sec)
+    },
+    /// Cancels the running sequence and can be used to mark the end of a sequence
+    /// instead of using a number of Release() events
+    Complete,
+}
+
+/// The different types of actions we support for key sequences/macros
 #[non_exhaustive]
 #[derive(Clone, Copy)]
 pub enum HoldTapConfig {
@@ -195,6 +217,13 @@ where
     DefaultLayer(usize),
     /// Perform different actions on key hold/tap (see [`HoldTapAction`]).
     HoldTap(&'static HoldTapAction<T>),
+    /// A sequence of SequenceEvents
+    Sequence {
+        /// An array of SequenceEvents that will be triggered (in order)
+        events: &'static [SequenceEvent],
+    },
+    /// Cancels any running sequences
+    CancelSequences,
     /// Custom action.
     ///
     /// Define a user defined action. This enum can be anything you
diff --git a/src/layout.rs b/src/layout.rs
index dd9d1c5..20b5f41 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -46,7 +46,7 @@
 /// ```
 pub use keyberon_macros::*;
 
-use crate::action::{Action, HoldTapConfig, HoldTapAction};
+use crate::action::{Action, HoldTapConfig, HoldTapAction, SequenceEvent};
 use crate::key_code::KeyCode;
 use arraydeque::ArrayDeque;
 use heapless::Vec;
@@ -68,6 +68,9 @@ pub type Layers<const C: usize, const R: usize, const L: usize, T = core::conver
 /// Events can be retrieved by iterating over this struct and calling [Stacked::event].
 type Stack = ArrayDeque<[Stacked; 16], arraydeque::behavior::Wrapping>;
 
+// The maximum number of simultaneously-executing Squences:
+const MAX_SEQUENCES: usize = 4;
+
 /// The layout manager. It takes `Event`s and `tick`s as input, and
 /// generate keyboard reports.
 pub struct Layout<const C: usize, const R: usize, const L: usize, T = core::convert::Infallible>
@@ -80,6 +83,8 @@ where
     waiting: Option<WaitingState<T>>,
     stacked: Stack,
     tap_hold_tracker: TapHoldTracker,
+    active_sequences:
+        ArrayDeque<[SequenceState; MAX_SEQUENCES], arraydeque::behavior::Wrapping>,
 }
 
 /// An event on the key matrix.
@@ -175,6 +180,7 @@ enum State<T: 'static> {
     NormalKey { keycode: KeyCode, coord: (u8, u8) },
     LayerModifier { value: usize, coord: (u8, u8) },
     Custom { value: &'static T, coord: (u8, u8) },
+    FakeKey { keycode: KeyCode }, // Fake key event for sequences
 }
 impl<T> Copy for State<T> {}
 impl<T> Clone for State<T> {
@@ -186,6 +192,7 @@ impl<T: 'static> State<T> {
     fn keycode(&self) -> Option<KeyCode> {
         match self {
             NormalKey { keycode, .. } => Some(*keycode),
+            FakeKey { keycode } => Some(*keycode),
             _ => None,
         }
     }
@@ -202,6 +209,12 @@ impl<T: 'static> State<T> {
             _ => Some(*self),
         }
     }
+    fn seq_release(&self, kc: KeyCode) -> Option<Self> {
+        match *self {
+            FakeKey { keycode, .. } if keycode == kc => None,
+            _ => Some(*self),
+        }
+    }
     fn get_layer(&self) -> Option<usize> {
         match self {
             LayerModifier { value, .. } => Some(*value),
@@ -293,6 +306,14 @@ impl<'a> Iterator for StackedIter<'a> {
     }
 }
 
+#[derive(Debug, Copy, Clone)]
+struct SequenceState {
+    cur_event: Option<SequenceEvent>,
+    delay: u32,              // Keeps track of SequenceEvent::Delay time remaining
+    tapped: Option<KeyCode>, // Keycode of a key that should be released at the next tick
+    remaining_events: &'static [SequenceEvent],
+}
+
 /// An event, waiting in a stack to be processed.
 #[derive(Debug)]
 pub struct Stacked {
@@ -337,6 +358,7 @@ impl<const C: usize, const R: usize, const L: usize, T: 'static> Layout<C, R, L,
             waiting: None,
             stacked: ArrayDeque::new(),
             tap_hold_tracker: Default::default(),
+            active_sequences: ArrayDeque::new(),
         }
     }
     /// Iterates on the key codes of the current state.
@@ -380,6 +402,7 @@ impl<const C: usize, const R: usize, const L: usize, T: 'static> Layout<C, R, L,
         self.states = self.states.iter().filter_map(State::tick).collect();
         self.stacked.iter_mut().for_each(Stacked::tick);
         self.tap_hold_tracker.tick();
+        self.process_sequences();
         match &mut self.waiting {
             Some(w) => match w.tick(&self.stacked) {
                 Some(WaitingAction::Hold) => self.waiting_into_hold(),
@@ -393,6 +416,84 @@ impl<const C: usize, const R: usize, const L: usize, T: 'static> Layout<C, R, L,
             },
         }
     }
+    /// Takes care of draining and populating the `active_sequences` ArrayDeque,
+    /// giving us sequences (aka macros) of nearly limitless length!
+    fn process_sequences(&mut self) {
+        // Iterate over all active sequence events
+        for _ in 0..self.active_sequences.len() {
+            if let Some(mut seq) = self.active_sequences.pop_front() {
+                // If we've encountered a SequenceEvent::Delay we must count
+                // that down completely before doing anything else...
+                if seq.delay > 0 {
+                    seq.delay = seq.delay.saturating_sub(1);
+                } else if let Some(keycode) = seq.tapped {
+                    // Clear out the Press() matching this Tap()'s keycode
+                    self.states = self
+                        .states
+                        .iter()
+                        .filter_map(|s| s.seq_release(keycode))
+                        .collect();
+                    seq.tapped = None;
+                } else {
+                    // Pull the next SequenceEvent
+                    match seq.remaining_events {
+                        [e, tail @ ..] => {
+                            seq.cur_event = Some(*e);
+                            seq.remaining_events = tail;
+                        }
+                        [] => (),
+                    }
+                    // Process it (SequenceEvent)
+                    match seq.cur_event {
+                        Some(SequenceEvent::Complete) => {
+                            for fake_key in self.states.clone().iter() {
+                                match *fake_key {
+                                    FakeKey { keycode } => {
+                                        self.states = self
+                                            .states
+                                            .iter()
+                                            .filter_map(|s| s.seq_release(keycode))
+                                            .collect();
+                                    }
+                                    _ => {}
+                                }
+                            }
+                            seq.remaining_events = &[];
+                        }
+                        Some(SequenceEvent::Press(keycode)) => {
+                            // Start tracking this fake key Press() event
+                            let _ = self.states.push(FakeKey { keycode });
+                        }
+                        Some(SequenceEvent::Tap(keycode)) => {
+                            // Same as Press() except we track it for one tick via seq.tapped:
+                            let _ = self.states.push(FakeKey { keycode });
+                            seq.tapped = Some(keycode);
+                        }
+                        Some(SequenceEvent::Release(keycode)) => {
+                            // Clear out the Press() matching this Release's keycode
+                            self.states = self
+                                .states
+                                .iter()
+                                .filter_map(|s| s.seq_release(keycode))
+                                .collect()
+                        }
+                        Some(SequenceEvent::Delay { duration }) => {
+                            // Setup a delay that will be decremented once per tick until 0
+                            if duration > 0 {
+                                // -1 to start since this tick counts
+                                seq.delay = duration - 1;
+                            }
+                        }
+                        _ => {} // We'll never get here
+                    }
+                }
+                if seq.remaining_events.len() > 0 {
+                    // Put it back
+                    self.active_sequences.push_back(seq);
+                }
+            }
+        }
+    }
     fn unstack(&mut self, stacked: Stacked) -> CustomEvent<T> {
         use Event::*;
         match stacked.event {
@@ -493,6 +594,30 @@ impl<const C: usize, const R: usize, const L: usize, T: 'static> Layout<C, R, L,
                 }
                 return custom;
             }
+            Sequence { events } => {
+                self.active_sequences.push_back(SequenceState {
+                    cur_event: None,
+                    delay: 0,
+                    tapped: None,
+                    remaining_events: events,
+                });
+            }
+            CancelSequences => {
+                // Clear any and all running sequences then clean up any leftover FakeKey events
+                self.active_sequences.clear();
+                for fake_key in self.states.clone().iter() {
+                    match *fake_key {
+                        FakeKey { keycode } => {
+                            self.states = self
+                                .states
+                                .iter()
+                                .filter_map(|s| s.seq_release(keycode))
+                                .collect();
+                        }
+                        _ => {}
+                    }
+                }
+            }
             &Layer(value) => {
                 self.tap_hold_tracker.coord = coord;
                 let _ = self.states.push(LayerModifier { value, coord });
@@ -534,6 +659,7 @@ mod test {
     use super::{Event::*, Layout, *};
     use crate::action::Action::*;
     use crate::action::HoldTapConfig;
+    use crate::action::SequenceEvent;
     use crate::action::{k, l, m};
     use crate::key_code::KeyCode;
     use crate::key_code::KeyCode::*;
@@ -1215,4 +1341,206 @@ mod test {
             assert_keys(&[Enter], layout.keycodes());
         }
     }
+
+    #[test]
+    fn sequences() {
+        static LAYERS: Layers<4, 1, 1> = [[[
+            Sequence {
+                // Simple Ctrl-C sequence/macro
+                events: &[
+                    SequenceEvent::Press(LCtrl),
+                    SequenceEvent::Press(C),
+                    SequenceEvent::Release(C),
+                    SequenceEvent::Release(LCtrl),
+                ],
+            },
+            Sequence {
+                // So we can test that Complete works
+                events: &[
+                    SequenceEvent::Press(LCtrl),
+                    SequenceEvent::Press(C),
+                    SequenceEvent::Complete,
+                ],
+            },
+            Sequence {
+                // YO with a delay in the middle
+                events: &[
+                    SequenceEvent::Press(Y),
+                    SequenceEvent::Release(Y),
+                    // "How many licks does it take to get to the center?"
+                    SequenceEvent::Delay { duration: 3 }, // Let's find out
+                    SequenceEvent::Press(O),
+                    SequenceEvent::Release(O),
+                ],
+            },
+            Sequence {
+                // A long sequence to test the chunking capability
+                events: &[
+                    SequenceEvent::Press(LShift), // Important: Shift must remain held
+                    SequenceEvent::Press(U),      // ...or the message just isn't the same!
+                    SequenceEvent::Release(U),
+                    SequenceEvent::Press(N),
+                    SequenceEvent::Release(N),
+                    SequenceEvent::Press(L),
+                    SequenceEvent::Release(L),
+                    SequenceEvent::Press(I),
+                    SequenceEvent::Release(I),
+                    SequenceEvent::Press(M),
+                    SequenceEvent::Release(M),
+                    SequenceEvent::Press(I),
+                    SequenceEvent::Release(I),
+                    SequenceEvent::Press(T),
+                    SequenceEvent::Release(T),
+                    SequenceEvent::Press(E),
+                    SequenceEvent::Release(E),
+                    SequenceEvent::Press(D),
+                    SequenceEvent::Release(D),
+                    SequenceEvent::Press(Space),
+                    SequenceEvent::Release(Space),
+                    SequenceEvent::Press(P),
+                    SequenceEvent::Release(P),
+                    SequenceEvent::Press(O),
+                    SequenceEvent::Release(O),
+                    SequenceEvent::Press(W),
+                    SequenceEvent::Release(W),
+                    SequenceEvent::Press(E),
+                    SequenceEvent::Release(E),
+                    SequenceEvent::Press(R),
+                    SequenceEvent::Release(R),
+                    SequenceEvent::Press(Kb1),
+                    SequenceEvent::Release(Kb1),
+                    SequenceEvent::Press(Kb1),
+                    SequenceEvent::Release(Kb1),
+                    SequenceEvent::Press(Kb1),
+                    SequenceEvent::Release(Kb1),
+                    SequenceEvent::Press(Kb1),
+                    SequenceEvent::Release(Kb1),
+                    SequenceEvent::Release(LShift),
+                ],
+            },
+        ]]];
+        let mut layout = Layout::new(&LAYERS);
+        // Test a basic sequence
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[], layout.keycodes());
+        layout.event(Press(0, 0));
+        // Sequences take an extra tick to kickoff since the first tick starts the sequence:
+        assert_eq!(CustomEvent::NoEvent, layout.tick()); // Sequence detected & added
+        assert_eq!(CustomEvent::NoEvent, layout.tick()); // Sequence starts
+        assert_keys(&[LCtrl], layout.keycodes()); // First item in the SequenceEvent
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LCtrl, C], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LCtrl], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[], layout.keycodes());
+        // Test the use of Complete()
+        assert_keys(&[], layout.keycodes());
+        layout.event(Press(0, 1));
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LCtrl], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LCtrl, C], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[], layout.keycodes());
+        // Test a sequence with a Delay() (aka The Mr Owl test; duration == 3)
+        layout.event(Press(0, 2));
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[Y], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick()); // First decrement (2)
+        assert_keys(&[], layout.keycodes()); // "Eh Ooone!"
+        assert_eq!(CustomEvent::NoEvent, layout.tick()); // Second decrement (1)
+        assert_keys(&[], layout.keycodes()); // "Eh two!"
+        assert_eq!(CustomEvent::NoEvent, layout.tick()); // Final decrement (0)
+        assert_keys(&[], layout.keycodes()); // "Eh three."
+        assert_eq!(CustomEvent::NoEvent, layout.tick()); // Press() added for the next tick()
+        assert_eq!(CustomEvent::NoEvent, layout.tick()); // FakeKey Press()
+        assert_keys(&[O], layout.keycodes()); // CHOMP!
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[], layout.keycodes());
+        // // Test really long sequences (aka macros)...
+        layout.event(Press(0, 3));
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, U], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, N], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, L], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, I], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, M], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, I], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, T], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, E], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, D], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, Space], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, P], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, O], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, W], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, E], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, R], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, Kb1], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, Kb1], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, Kb1], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift, Kb1], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+        assert_keys(&[LShift], layout.keycodes());
+        assert_eq!(CustomEvent::NoEvent, layout.tick());
+    }
 }