If you use a MacBook trackpad, you may have noticed an annoying behavior on web pages that can scroll both vertically and horizontally: when you move two fingers mostly up or down, even a tiny sideways movement can make the page drift left or right. The opposite can happen during horizontal scrolling too.
What you probably want is simple: when a two-finger scroll gesture starts, macOS should decide whether you meant to scroll vertically or horizontally, then ignore movement on the other axis until you lift your fingers.
This behavior is usually called scroll axis locking or axis lock. macOS Sequoia does not provide a built-in setting for it, but you can add it globally with Hammerspoon.
What this fix does
With this setup:
- If a gesture starts mostly vertically, horizontal movement is ignored until the gesture ends.
- If a gesture starts mostly horizontally, vertical movement is ignored until the gesture ends.
- Horizontal scrolling is not disabled: it still works when you deliberately start a horizontal gesture.
- The behavior applies system-wide, not only to your web browser.
This is especially useful on websites with horizontally scrollable elements, wide tables, carousels, timelines, code blocks, kanban boards, or other layouts where a small diagonal trackpad movement can shift the page unexpectedly.
Install Hammerspoon
Download and install Hammerspoon from its official website:
Launch it once. macOS may ask you to give Hammerspoon permission under:
System Settings → Privacy & Security → Accessibility
Enable Hammerspoon there. This permission is required because the script needs to inspect and modify scroll events.
Create the Hammerspoon configuration
Hammerspoon reads its configuration from:
~/.hammerspoon/init.luaCreate that file if it does not already exist, then paste the following code into it:
-- Trackpad Scroll Axis Lock
--
-- Each new two-finger scroll gesture is locked to its
-- dominant axis: vertical OR horizontal.
local eventtap = hs.eventtap
local event = hs.eventtap.event
local props = event.properties
local lockedAxis = nil
-- 1.0 = choose the mathematically dominant axis.
-- 1.10 = slightly favor vertical scrolling.
-- Increase this value if you mostly scroll vertically
-- and want horizontal scrolling to require a clearer gesture.
local verticalBias = 1.10
local function zeroHorizontal(e)
e:setProperty(props.scrollWheelEventDeltaAxis2, 0)
e:setProperty(props.scrollWheelEventFixedPtDeltaAxis2, 0)
e:setProperty(props.scrollWheelEventPointDeltaAxis2, 0)
end
local function zeroVertical(e)
e:setProperty(props.scrollWheelEventDeltaAxis1, 0)
e:setProperty(props.scrollWheelEventFixedPtDeltaAxis1, 0)
e:setProperty(props.scrollWheelEventPointDeltaAxis1, 0)
end
scrollAxisLock = eventtap.new(
{ event.types.scrollWheel },
function(e)
local phase =
e:getProperty(props.scrollWheelEventScrollPhase)
local momentumPhase =
e:getProperty(props.scrollWheelEventMomentumPhase)
-- A new gesture has started.
if phase == 1 then
lockedAxis = nil
end
local dy =
e:getProperty(props.scrollWheelEventFixedPtDeltaAxis1)
local dx =
e:getProperty(props.scrollWheelEventFixedPtDeltaAxis2)
-- On the first real movement, choose the axis.
if lockedAxis == nil and (dx ~= 0 or dy ~= 0) then
local absX = math.abs(dx)
local absY = math.abs(dy)
if absX > absY * verticalBias then
lockedAxis = "horizontal"
else
lockedAxis = "vertical"
end
end
-- Completely remove movement on the other axis.
if lockedAxis == "vertical" then
zeroHorizontal(e)
elseif lockedAxis == "horizontal" then
zeroVertical(e)
end
-- Reset after momentum scrolling ends.
if momentumPhase == 3 then
lockedAxis = nil
end
-- Reset if the gesture is cancelled.
if phase == 8 then
lockedAxis = nil
end
-- Let the modified event continue to the application.
return false
end
)
scrollAxisLock:start()Reload the configuration
If Hammerspoon opens only the Hammerspoon Console, that is fine. Type this command into the console and press Return:
hs.reload()Hammerspoon will immediately reload ~/.hammerspoon/init.lua.
You can close the Hammerspoon Console window afterward. Closing the console does not quit Hammerspoon and does not stop the scroll filter. Just avoid choosing Quit Hammerspoon, because that would stop the script.
Make Hammerspoon start automatically when you log in
Run this once in the Hammerspoon Console:
hs.autoLaunch(true)To check whether automatic launch is enabled, run:
hs.autoLaunch()If the console returns:
trueHammerspoon is configured to start automatically when you log in to macOS.
Optional: show a confirmation when the script loads
If you want a visible confirmation whenever the configuration is loaded, add this line to the end of init.lua:
hs.alert.show("Scroll Axis Lock enabled")Each time Hammerspoon starts or you reload the configuration, a small notification will appear briefly.
Optional: add a keyboard shortcut to reload Hammerspoon
If you expect to tweak the script, add this to init.lua:
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "R", function()
hs.reload()
end)You can then reload the configuration at any time with:
Command + Option + Control + R
Adjust how easily horizontal scrolling is selected
The most useful setting to tune is:
local verticalBias = 1.10At 1.0, the script simply chooses whichever axis has the larger initial movement.
local verticalBias = 1.0If you mostly scroll vertically and want accidental horizontal scrolling to be even less likely, try a higher value such as:
local verticalBias = 1.25With a higher value, a horizontal gesture must be more clearly horizontal before the script locks onto that axis.
How the axis lock works
Without this script, a slightly diagonal two-finger movement might produce a scroll event similar to:
Horizontal: +4
Vertical: +12A web page that supports scrolling in both directions can react to both values, so it moves vertically and sideways at the same time.
With axis locking enabled, the script sees that the vertical component dominates and changes the event to:
Horizontal: 0
Vertical: +12It keeps the horizontal component at zero for the rest of that gesture, even if your fingers drift slightly sideways. When you lift your fingers, the lock resets.
If the next gesture starts mostly horizontally, the opposite happens:
Horizontal: +15
Vertical: 0This gives the trackpad a much more deliberate feel on pages and applications that can scroll in two dimensions.
Why not simply disable horizontal scrolling?
Disabling horizontal scrolling entirely would also prevent intentional two-finger horizontal scrolling. That can be inconvenient in wide documents, timelines, tables, image editors, spreadsheets, and other applications.
Axis locking is more useful because it keeps both directions available while preventing an individual gesture from wandering diagonally.
Result
After this configuration is active, every two-finger trackpad scroll gesture is effectively treated as either vertical or horizontal, never both at the same time.
If you arrived here because your Mac trackpad makes web pages move sideways while you are trying to scroll up or down, this is the behavior you were looking for: scroll axis locking.
(August 31, 2026)