any recoil. Reads a mouse event node (read-only, no exclusive grab)
and, on every LMB press, injects counter-movements through a uinput
virtual device to cancel a recoil pattern. Patterns load from JSON at
runtime, no rebuild to switch weapons.
Ships with 19 pre-converted weapon patterns in weapons/the-finals/.
anyr.mp4
If anyr is useful to you, consider starring the repo. This is a solo project and a star genuinely helps with visibility.
AHK and most Windows recoil scripts call SendInput/mouse_event through
the Windows API. Kernel anti-cheats (EAC, Vanguard, BE) sit in the driver
stack and can see those calls coming from a process that isn't the game,
which is what gets them flagged.
anyr works differently. It creates a uinput virtual device, which the
kernel presents as a real hardware device node identical to a second
physical mouse being plugged in. The anti-cheat sees mouse events arriving
from what looks like legitimate hardware. anyr never touches the game
process, never reads its memory, and opens your real mouse read-only with
no exclusive grab.
Tested across multiple games with kernel-level anti-cheat without detection.
- Linux, kernel with
uinputsupport (virtually all distros ship this). There's no Windows/macOS path:vmouse.ctalks to/dev/uinputdirectly andmain.creads rawlinux/input.hevents, both Linux-only APIs. gccandmake. cJSON is vendored inthird_party/, no other system packages needed.pthread(part of glibc on any modern distro, nothing extra to install).- Root, or equivalent permissions, at runtime: reading
/dev/input/eventNand creating a device via/dev/uinputboth need it. Either run withsudo, or set up a udev rule granting your user access to both nodes if you'd rather not run as root. - Python 3 only if you use
src/calc_sensitivity.py, no dependencies beyond the standard library.
python3 src/calc_sensitivity.py
Prompts for your in-game sensitivity, computes SENSITIVITY_CPD from
your game's yaw constant (defaults to The Finals, 0.001, edit YAW
at the top of the script for a different game), and patches
src/config.h directly. You can re-run this any time and make again
to update.
Common yaw constants:
| Game | Yaw |
|---|---|
| The Finals | 0.001 |
| CS2 | 0.022 |
| Valorant | 0.07 |
| Overwatch 2 | 0.0066 |
make
Requires gcc and pthread. cJSON is vendored in third_party/, no
system packages needed.
sudo ./anyr # scan for mice
sudo ./anyr /dev/input/eventN # keyboard auto-detected
sudo ./anyr /dev/input/eventN /dev/input/eventKBD [dir] # explicit keyboard + patterns folder
By default anyr looks in weapons/ (see PATTERNS_DIR in src/config.h).
If that folder contains subfolders, each one is treated as a game/mod and
you get a numbered menu to pick one first:
Available games in weapons:
1) the-finals
2) some-other-game
Select a game (1-2, or q to quit):
Then, same as before, a numbered menu of that game's *.json patterns:
Available patterns in weapons/the-finals:
1) 93r.json
2) akm.json
3) arn-220.json
...
If weapons/ has no subfolders, just .json files dropped straight in
(handy while testing a new pattern), the game step is skipped entirely and
you go straight to the weapon menu, same as the old flat layout.
A typo re-prompts, it won't kill the program. q or Ctrl+D exits.
Switching mid-session: press the switch key (default P) any time
you're not holding LMB, and the weapon menu reopens (within whatever game
folder you picked at startup, no need to re-pick the game) without
restarting anyr or losing your virtual device. No-op while firing,
release LMB first.
Important: if another process already holds
EVIOCGRABon the physical mouse, that device delivers events only to the grabber, so anyr sees nothing on it. Point anyr at that process's virtual output device instead. Run with no args to list all mouse-like nodes, physical and virtual.
Use S4N-T0S/recoil-analyser to generate the JSON. It records a clip of a full magazine fired at a tagged wall, tracks the wall to recover the actual recoil path, and outputs a ready-to-use JSON in this schema. Works for any FPS. See its README for the recording protocol.
Drop the resulting file into weapons/<game>/, no build step needed. A
bad or malformed file fails with a clear message and leaves whatever
pattern was already loaded untouched.
The fields anyr reads from each JSON:
weapon: shown in the banner and menucapture.fpspattern[]: cumulativedx_deg/dy_degper bullet, converted internally to per-step incremental deltasrpm.intervals_frames[]+capture.fps: converted to per-step delay in microseconds
All in src/config.h:
| Macro | Meaning |
|---|---|
SENSITIVITY_CPD |
Mouse counts per degree, set via calc_sensitivity.py or by hand |
INVERT_X / INVERT_Y |
Flip an axis if the counter moves the wrong direction |
PATTERNS_DIR |
Default folder scanned for *.json (3rd CLI arg overrides) |
SWITCH_KEY / SWITCH_KEY_NAME |
Key that reopens the pattern menu |
pattern_menu_choose()lists*.jsonin the target folder, blocks on stdin for a number.pattern_load()parses the JSON (via vendored cJSON), converts cumulative deltas to per-step deltas and frame intervals to microseconds, returns a heap-allocatedpattern_t.- On LMB press, a thread walks the steps with absolute-time scheduling
(
clock_nanosleep(..., TIMER_ABSTIME, ...)against one fixed anchor). Chaining relativeusleep()calls would let each step's overhead bleed into the next step's sleep, and that drift compounds across a full magazine. One absolute anchor means it never accumulates. - Each step emits
REL_X = -round(dx_deg * CPD),REL_Y = -round(dy_deg * CPD), with a float accumulator carrying the rounding remainder to the next step. - Stops on LMB release or when the pattern ends.
- The switch key reopens the menu, waits (bounded) for any in-flight replay thread to finish, then frees the old pattern and loads the new one. Refused outright while LMB is held.
MIT, see LICENSE. cJSON (third_party/) is separately
MIT-licensed by Dave Gamble and contributors.