Perfecting Mouse Acceleration on Hyprland: Reproducing the Windows Feel

TL;DR: If you miss Windows’ smooth pointer acceleration on Linux, Hyprland lets you recreate it using a custom accel_profile. This guide explains how to generate your own curve using a Python script and apply it to achieve a natural, responsive mouse feel for both gaming and development.

Configuring mouse acceleration on Hyprland

For years, I’ve switched back and forth between Windows and Linux, developing, gaming, and constantly comparing the small nuances between them. While Windows has its flaws, it gets one thing really right: mouse acceleration.

When I finally switched 100% to Linux (Wayland + Hyprland), one of the first things that bugged me was how unnatural mouse movement felt. The default linear acceleration on Linux just didn’t cut it. As someone who alternates between coding precision and fast-paced gaming, I needed a proper acceleration curve, something closer to what Windows provides.


Discovering Hyprland’s Acceleration Profiles

After a bit of digging, I stumbled upon the accel_profile setting under Hyprland’s input configuration block:
https://wiki.hypr.land/Configuring/Variables/#input

The documentation explains the basics, but in reality, the examples provided there won’t give you anything close to Windows’ “Enhance pointer precision” behavior. In fact, they often feel either too slow or too unpredictable.

Here’s an example of a default Hyprland input block before customization:

input {
    sensitivity = 0
    accel_profile = linear
    follow_mouse = 1
}

Some users argue that a linear profile is objectively better, predictable, consistent, easier for muscle memory. I get that. But the human brain is a pattern-learning machine. Once you’ve spent years using Windows acceleration, your reflexes are literally tuned to that specific non-linear curve. Trying to unlearn that is harder than it seems.

That’s why, after several days of trying to adapt, I decided to do the opposite: bring Windows-like acceleration to Hyprland.


Understanding libinput’s Acceleration Model

To truly grasp how this all works, it helps to understand that Hyprland’s pointer acceleration system is built directly on libinput, the same backend used by GNOME, KDE, and most modern Wayland compositors.

libinput defines three acceleration profiles:

  • adaptive: the default profile that adjusts acceleration dynamically based on pointer velocity.
  • flat: a simple 1:1 mapping between device and pointer motion (no acceleration).
  • custom: a fully user-defined curve, giving complete control over pointer behavior.

The custom acceleration profile exposes an f(x) function that maps device velocity (in units per millisecond) to output velocity. Each point defines how acceleration behaves at different speeds, and libinput linearly interpolates between them. Hyprland’s accel_profile = custom ... syntax is a direct implementation of this concept.

📘 Reference: libinput Pointer Acceleration Documentation

Example visualization

Below is a conceptual comparison between a linear acceleration curve (default) and a custom Windows-style curve generated with this script:

Linear vs Custom Pointer Acceleration Curves

The linear curve increases at a constant rate, while the Windows-style custom curve ramps more aggressively at higher velocities, creating that familiar “flick and glide” responsiveness.


Reproducing the Windows Curve with Python

After some research, I found this brilliant script from fufexan on GitHub:

It’s an adaptation of an older script by yinonburgansky:

This Python script allows you to define your mouse DPI, screen DPI, scaling factor, and sensitivity. Each parameter influences how the curve behaves:

  • Mouse DPI: defines your base precision.
  • Screen DPI: adjusts perceived movement speed relative to resolution.
  • Scaling factor: compensates for display scaling (e.g., HiDPI setups).
  • Sensitivity: controls the steepness and intensity of acceleration.

It then generates a smooth set of acceleration curve points that you can directly plug into your Hyprland config.


The Python Script

Now that you understand how the curve generation works, here’s the complete Python script I use to replicate the Windows pointer acceleration on Hyprland. It’s lightweight, configurable, and can even apply the new profile instantly using hyprctl.

If you want a self-contained version of the tool, here’s the full Python script adapted for Hyprland. It’s based on the original Windows acceleration calculation formulas, with modifications to make it plug-and-play for Linux users.

#!/usr/bin/env python3

# original at https://gist.github.com/yinonburgansky/7be4d0489a0df8c06a923240b8eb0191
# modified for ease of use in Hyprland

# calculations are based on http://www.esreality.com/index.php?a=post&id=1945096
# assuming Windows 10 uses the same calculation as Windows 7.
# guesses have been made — calculation is not 100% accurate.
# touchpad users: ensure your touchpad is calibrated with `sudo libinput measure touchpad-size`

import struct
import os
import sys
# import matplotlib.pyplot as plt  # Optional: uncomment for curve visualization

# ===== PARAMETERS =====
# Set these according to your device:
device_dpi = 1000  # Mouse DPI
screen_dpi = 96
screen_scaling_factor = 1
sample_point_count = 20  # Increase for more accurate curves
sensitivity_factor = 6   # See table below
# Sensitivity factor translation (Windows slider notches):
# 1 = 0.1 | 2 = 0.2 | 3 = 0.4 | 4 = 0.6 | 5 = 0.8 | 6 = 1.0 (default)
# 7 = 1.2 | 8 = 1.4 | 9 = 1.6 | 10 = 1.8 | 11 = 2.0
# ===== END PARAMETERS =====

def find_arg(arg):
    return arg in sys.argv

if find_arg("help") or find_arg("-h") or find_arg("--help") or find_arg("h"):
    print(f'{sys.argv[0]} [[accel_profile] [scroll_points] device=<device>]')
    print('To get the device, run `hyprctl devices` and get its name')
    exit(0)

# Scale factors (approximate Windows model)
scale_x = device_dpi / 1e3
scale_y = screen_dpi / 1e3 / screen_scaling_factor * sensitivity_factor

def float16x16(num):
    return struct.unpack('<i', num[:-4])[0] / int(0xffff)

# Windows 10 registry curve points
X = [
    bytes.fromhex("00 00 00 00 00 00 00 00"),
    bytes.fromhex("15 6e 00 00 00 00 00 00"),
    bytes.fromhex("00 40 01 00 00 00 00 00"),
    bytes.fromhex("29 dc 03 00 00 00 00 00"),
    bytes.fromhex("00 00 28 00 00 00 00 00"),
]
Y = [
    bytes.fromhex("00 00 00 00 00 00 00 00"),
    bytes.fromhex("fd 11 01 00 00 00 00 00"),
    bytes.fromhex("00 24 04 00 00 00 00 00"),
    bytes.fromhex("00 fc 12 00 00 00 00 00"),
    bytes.fromhex("00 c0 bb 01 00 00 00 00"),
]

windows_points = [[float16x16(x), float16x16(y)] for x, y in zip(X, Y)]
points = [[x * scale_x, y * scale_y] for x, y in windows_points]

def get_device():
    for i in sys.argv:
        if str(i).startswith('device='):
            return str(i)[7:]

def find2points(x):
    i = 0
    while i < len(points) - 2 and x >= points[i + 1][0]:
        i += 1
    return points[i], points[i + 1]

def interpolate(x):
    (x0, y0), (x1, y1) = find2points(x)
    return ((x - x0) * y1 + (x1 - x) * y0) / (x1 - x0)

def sample_points(count):
    last_point = -2
    max_x = points[last_point][0]
    step = max_x / (count + last_point)
    sample_points_x = [si * step for si in range(count)]
    sample_points_y = [interpolate(x) for x in sample_points_x]
    return sample_points_x, sample_points_y

sample_points_x, sample_points_y = sample_points(sample_point_count)
step = sample_points_x[1] - sample_points_x[0]
sample_points_str = " ".join(["%.3f" % number for number in sample_points_y])

print(f'\tPoints: {sample_points_str}')
print(f'\tStep size: {step:0.10f}')

def hyprctl(device, option, arg):
    os.system(f"hyprctl keyword 'device[{device}]:{option}' '{arg}'")

if find_arg("accel_profile"):
    device = get_device()
    print(f"Setting device '{device}':accel_profile using hyprctl")
    hyprctl(device, 'accel_profile', f'custom {step} {sample_points_str}')

if find_arg("scroll_points"):
    device = get_device()
    print(f"Setting device '{device}':scroll_points using hyprctl")
    hyprctl(device, 'scroll_points', f'{step} {sample_points_str}')

After saving it as ~/.config/hypr/conf/accel.py, make it executable:

chmod +x ~/.config/hypr/conf/accel.py

Then run it normally or target a specific device:

~/.config/hypr/conf/accel.py custom 2 device=logitech-pro-x-wireless-1

This script generates a realistic Windows-style acceleration curve and even lets you apply it instantly using hyprctl. You can tweak the DPI and sensitivity values to fine-tune your experience across multiple devices.


Running the Script

Here’s what it looks like in action:

❯ ~/.config/hypr/conf/accel.py
Points: 0.000 0.194 0.388 0.583 0.777 1.046 1.338 1.630 1.922 2.214 2.507 2.799 3.091 3.488 3.932 4.375 4.819 5.263 5.707 6.150 6.594 7.038 7.481 7.925 8.369 8.812 9.256 9.700 10.143 10.587 11.031 11.474 11.918 12.362 12.805 13.249 13.693 14.137 14.580 15.497
Step size: 0.1625288215

This gives you the acceleration curve points and the step size value required by Hyprland.


Applying the Configuration

Once you have the values, add them to your Hyprland input configuration file, for example:

cat ~/.config/hypr/conf/keyboard.conf | grep -i accel
    accel_profile = custom 0.1625288215 0.000 0.194 0.388 0.583 0.777 1.046 1.338 1.630 1.922 2.214 2.507 2.799 3.091 3.488 3.932 4.375 4.819 5.263 5.707 6.150 6.594 7.038 7.481 7.925 8.369 8.812 9.256 9.700 10.143 10.587 11.031 11.474 11.918 12.362 12.805 13.249 13.693 14.137 14.580 15.497

Notice the key detail: the keyword custom must come first, immediately followed by the step size, then the curve points.

After saving your configuration, reload Hyprland to apply the changes:

hyprctl reload

This results in a much more natural acceleration feel—smooth, predictable, and familiar to anyone who’s used to Windows’ behavior.


Optional: Target a Specific Device

You can also specify the device directly when running the script:

~/.config/hypr/conf/accel.py custom 2 device=logitech-pro-x-wireless-1

To find your mouse identifier, use:

❯ hyprctl devices
mice:
	Mouse at 55d41bf937b0:
		logitech-pro-x-wireless
			default speed: 0.00000
			scroll factor: -1.00
	Mouse at 55d41e78e0c0:
		logitech-pro-x-wireless-2
			default speed: 0.00000
			scroll factor: -1.00

Once configured, reload Hyprland and enjoy the buttery-smooth movement.


Final Thoughts

This setup may sound like overkill for some, but small ergonomic details like pointer acceleration make a huge difference in comfort and precision. If your brain has adapted to a non-linear curve, fighting it will only create friction.

Everyone’s ideal curve will differ slightly based on DPI, resolution, and sensitivity. Experiment with your own parameters and don’t hesitate to share your configuration. There’s a growing community of Hyprland users tuning their setups for that perfect feel.

With this method, you can enjoy a perfect blend of Linux flexibility and the familiar mouse behavior you loved from Windows, making Hyprland feel both natural and responsive for gaming and development.

November 9, 2025 by Julien Turbide