You are here

How to Stop Diagonal Two-Finger Scrolling on macOS (Lock Trackpad Scrolling to One Axis)

Translate this articleSpeak this article

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:

https://www.hammerspoon.org/

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.lua

Create 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:

true

Hammerspoon 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.10

At 1.0, the script simply chooses whichever axis has the larger initial movement.

local verticalBias = 1.0

If you mostly scroll vertically and want accidental horizontal scrolling to be even less likely, try a higher value such as:

local verticalBias = 1.25

With 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:   +12

A 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:   +12

It 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:   0

This 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)

Classificazione: