i made an aim trainer
i made an aim trainer
hello.
A lot has happened in my life recently, and as it happens I'm accepting a new position in the birthplace of fallen dreams (san francisco).
Exciting as this is, it means I will have to work again. I've been trying to take advantage of my freedom while I have it, so I have returned to my love/hate relationship with the video game Valorant. There is one issue with this: I'm bad at it.
When I look at a game, I tend to divide it into micro and macro (for a much better explanation of what that means, this video is helpful). Applied to Valorant, this means that I am paranoid about having good aim. Unfortunately, I am very naturally uninclined towards this end.
The normal tool for this is an aim trainer, or just getting good. I've used aim trainers for quite a while. My favorite routine right now is the Viscose benchmarks in KovaaK's, and I think they are honestly really good.
However, as a perception engineer with a Cognitive Science degree and some dabbling in brain-computer interfaces, I felt like I may be uniquely equipped to make this problem much more complicated than it needed to be.
background
Most aim training works like so: somebody makes a playlist of scenarios, each one targets a certain skill, and you replay them until the number goes up. The hope being that this number represents better mouse control, which hopefully represents better aim, which even more hopefully transfers to the game you're playing.
I had two hangups with this norm. First, the scenarios are usually fixed while the player is not. Anyone that's played an older Voltaic playlist knows this feeling, where the gap between the scenarios in the easiest playlist and the next one sometimes feels insurmountable. However, humans generally improve quickest at a slight challenge point, and this is very sensitive per player. This is one of the good parts of Viscose, difficulty jumps much less and keeps the player anchored on improvement. Second, scores give an overall read of the targets you clicked, but not how. If you clicked on a target late, or your wrist was shaky, or you just got tired, the end result is still just a single number that provides insight on only the peak of your performance on this one specific scenario.
So I made openaim, a (free!) aim trainer that records your raw mouse movements and tries to estimate your actual motor proficiency skills with the mouse, instead of a score on a scenario. Then, an algorithmically-motivated coach generates drills to make scenarios that are just hard enough to foster improvement, in all the specific skills you're the worst at.
I wanted this article to be interesting to anyone interested in FPS games and aim trainers, as well as giga nerds like myself. This lent itself to a difficult to balance and technically dense writeup, so buckle up.
OpenAim's menu
how it's going
Before the specifics, here is a player who does not exist.
This is a 24-second run from a synthetic generated player on 2-Tap Strafe
how did we get here
Let me back up. Before any of the interesting modeling, there was a pile of unglamorous groundwork.
First of all, I needed to make a basic aim training environment. I wanted to really make sure this was snappy, so it runs on WebGL and can comfortably hit 2k+ fps on my 4080, this also allowed me more leeway to simulate it locally. Input fidelity is quite important, so I made sure that the browser takes inputs at as high of a fidelity as Chrome would allow me to. Latency is also measured and subtracted first class to get as accurate of a per-player fit as possible. In true CS2 fashion, the inputs are sub-tick as well!
Every kind of target the engine can spawn — a spread of sizes, spheroids, capsules, blinks, hit-flashes, beam-tracked locks, partial-HP rings — all at once while the camera sweeps across them. The FPS counter is real per-frame draw throughput, and this is captured software-rendered, so my 4080 does better.
It was pretty snappy, so I then moved to the crux of the problem: data.
It's incredibly useful to have a model and a dataset. With my industry being where it's at, it was very tempting to jump to a transformer, but it's good practice to first find the simplest model that can accurately represent your objective.
So the first natural question is, given a player's mouse inputs and an aim scenario, what can we estimate? The underlying system is a human using a chain of very complicated muscles along a feedback loop between your brain, arm, wrist, and fingertips. The output is literally just how much the mouse moves. So, given our run's raw mouse samples, camera angle, scenario targets, and sensitivity, we need to make a decent back estimation of what your actual hand is doing.
In order to do this, I tackled first the goliath Riot Games dared not to till recently: a replay system. This would allow me to make a dataset of myself, then test the fit of models without losing data. This means a run is not frozen to whatever scoring rule I happened upon when I played it. I can open it later, reconstruct the same view, and run tests in retrospect. So, how about that model?
a brief reminder that I went to college for this
Human motor control theory has been around for a long time, and scientists have been building models for how your perception-motor stack responds to inputs for decades. I decided that in the spirit of my background this would be a good place to start.
A fast aimed movement is not one perfectly smooth gesture. It usually has a large initial movement, often called the ballistic primary, followed by smaller corrections if the first landing was not good enough.1 Most members of the aim training community know this already, and there's a popular approach called the bardoz method that rests on this principle. This principle is actually quantifiably true, looking at a real 2kHz graph of one of my own flicks slowed down:
one flick, decomposed
a real capture — press playfig — one actual engagement, replayed from a captured .oar file: 19.99° amplitude, 228°/s peak crosshair speed, killed 542 ms after spawn. the four phases below are the segmenter's own output — reaction, ballistic launch, correction, settle — not hand-picked. played back 4× slower than it actually happened.
There is a second idea underneath this. Motor commands carry signal-dependent noise: as the command gets larger and faster, its endpoint becomes less consistent.2 This does not mean every person's hand obeys one magical straight line forever. It gives me a testable starting model:
where is your baseline scatter and is how much speed adds to it.
The useful word there is testable. We can devise a sort of algorithmic "motor head" responsible for fitting this per shot in physical hand space. It then checks whether the radial errors actually look like the distribution it assumes.
Now flicking is well accounted for, but tracking has its own version of this problem. The Servo-Gaussian work models continuous tracking as a chain of intermittent corrections with a corrective reaction time, instead of one continuous perfect feedback loop.3 That model predicts the experiments in its paper very well, but its population constants fit 3 of 12 participants poorly. This is kinda alarming, it means that (maybe unsurprisingly) reaction time and model mismatch must be measured per player. This made it abundantly clear that any model of a player needed to be hyper customized to their individual stats like reaction time, delay, etc.
Thus, with the help of some 50 (!)+ year old literature, we have some useful bootstraps for creating a model of a person's aim. Throwing some of these equations together, we have a relatively simple model of a player's aim that I've put into this playground below:
the servo-gaussian tracking model
readable and irregular motionfig — a readable toy of the Servo-Gaussian premise: tracking as a sequence of delayed corrective movements with roughly Gaussian motor error. this is not the paper's full fitted equation. turn on irregular reversals or occasional lapses and the neat assumption starts to come apart; that failure is something the model tests for instead of hiding.
building the coach
So we have a lot of data and funny math to form a diagnosis of a player. A diagnosis is only useful if you can do something with it, so what now?
Most aim trainers organize practice around named scenarios. However, we're trying to optimize for a specific person here. Instead of some premade scenarios, we need to build a generator.
OpenAim also has a small free-play menu, but underneath it a drill is a point in a continuous parameter space: target size, movement amplitude, speed, reversal rate, smoothness, target count, hold time, hits to kill, vertical motion, sensitivity, and even blinking and depth movement. When a reactive flick scenario is generated, it's a draw of a convenient neighborhood of that space.
This space also has a learned mapping between how each of the parameters relate to each player's ability to score on the scenario, so we have a direct way of predicting how well a player will do on any given scenario, as well as working backwards to estimate a player's skill given their performance on the scenario.
Say my last few runs were weak on unpredictable tracking. The player model first asks a "question" over our funny drill space that contains all aiming scenarios ever:
where is overall ability and is the player-specific cost of the drill's demands, summed across all fourteen.
There are fourteen rated demand axes in the player model vector, plus warm-up and fatigue terms. This makes sure that landing a far flick, intercepting a moving target, tracking a smooth one, reacting to a reversal, choosing between four live targets, and firing multiple controlled shots in a row are all different requests. Large physical mouse travel and tiny endpoint precision are different requests too. But in practice, these will be weakly correlated, because someone who is good at flicking will usually have some amount of necessary skill in tracking, so we try to learn the overlap of these between players as well. This lets me do some interesting statistics, like learning that a player with good micro-control is quicker to learn tracking on small targets. Because the data is n=1 (me) right now, these are a grain of salt, but still cool to know.
Each target engagement is projected into a fourteen-number demand vector: how much that moment asked for spatial precision, timing, reactivity, stability, switching, vertical control, arm-range control, micro control, fine-hand precision, hand speed, pace, smooth pursuit, reacquisition, and cadence. This sounds like a lot of words but it's basically just reverse-engineered from the parameters in scenarios that top aim training playlists tend to include. After all of this hubbub, the player model learns which of those demands are expensive for you.
here's a rough visual translation of the model:
a drill's fourteen demands
choose a scenarioStay attached to readable motion instead of chasing it.
Changes the hand-space demand, not the on-screen task.
Screen space
20° move · 2.2° targetHand space
1.94 cm move · 0.214 cm windowAdjust scenario geometry
Fourteen kinds of demand
raw load · bars cap at 1.5screen + timing
physical hand
control loop
Continuously cancelling error against motion that can be predicted rather than merely reacted to.
Loaded by smooth, predictable target motion.
fig — the same aim task in screen space, hand space, and the model's fourteen raw demand axes. the hand is a displacement proxy, not pose tracking.
Now, given all this, the coach has to pick the next drill. It already has a decent idea of your capabilities, so it draws one plausible version of your uncertain profile (Thompson sampling), then scores each candidate for two things: how much it should improve you, and how much it should teach the model about you.
where is a candidate drill and is the expected information gain: how much the outcome of that drill is expected to teach the player model.
Difficulty scaling is one of the most difficult parts of this problem statement. What is difficulty in an aiming sense anyways? I had to compromise and make my own heuristic, which is a graded probability between 0 and 1 of you taking the target "quickly" and "cleanly", which are basically budgets given per task for an expected good hit. For tracking problems, this is just continuous. We try to keep this number around 70%, to strike a good balance between infuriating and learnable — a number supported by literature in Challenge Point4 that says learning tends to be fastest here, and by the same adaptive learning systems that have been tested for years.5
Another important factor that many aim trainers struggle with is the fact that, usually, in FPS games, your opponents shoot back at you. This means that much of the time, you are forced to take a faster shot than optimal for accuracy, because otherwise you would die anyways. To help train for these situations, I also added scenarios that try to get the player to optimize their speed-accuracy frontier:
find your frontier
drag the point · tune σᵥfig — your speed–accuracy frontier. the slope σᵥ sets the fastest you can flick before neural noise, not skill, decides the outcome. where your point sits relative to it is what matters.
demystifying sensitivity
Sensitivity is where the hand-space idea gets interesting. As a middle-schooler growing up in the fps space, I always heard many different layman perspectives on what the perfect sens is, if you should change or never change it, if you should change it per weapon.
Fundamentally though, aiming is just a mouse control problem. Aiming at a target far away at high sens is the same as aiming at a target closer to you at low sens. More and more pros seem to have moved to this side of the argument, and so I wanted scenarios to directly plug into cm/360: how many centimeters the mouse must travel to turn the camera once. A larger number means a lower sensitivity.
For a movement of degrees, a target degrees wide, moving at degrees per second, the hand-space version is just:
where is cm/360. This means a 24° flick takes 1.33 cm at 20 cm/360 and 4 cm at 60 cm/360. At the faster sensitivity, the move is short but the target becomes a tiny physical landing window, loading micro control and fine-hand precision. At the slower sensitivity, the visual target is unchanged but the mouse has to travel farther and faster, loading arm-range control and hand speed. Neither is universally harder, but they are different parts of your motor control chain.
the same aim can ask a different hand question
pick a task · drag sensitivityscreen space
unchangedhand space
changesWhat the hand is being asked to do
raw demand · 0–2+The 0.078 cm landing window is the main hand-space constraint. Faster sensitivity would make it smaller.
fig — the angular task stays fixed while cm/360 changes its physical travel, landing window, and speed. the coach reasons over those hand-space demands; it does not advance through a sensitivity ladder.
I absolutely hated going from scenario to scenario in Kovaak's and constantly fiddling around with the sens to try to improve in scenarios. Thus, I aimed to make it something you no longer even think about, it is simply decided for you. It is one coordinate of the same drill vector as target size, distance, speed, and reversals.
That makes sensitivity an intelligent demand choice. If the model wants fine-hand precision, a faster sensitivity can shrink the physical landing window. If it wants arm-range control or hand speed, a slower one can stretch the same angular task across more mousepad.
So, instead of doing some dumb "this sens is your best" like Aimlabs, we can do something actually actionable: request a drill where your shaky wrist is exposed, while keeping the scenario possible and not frustrating. As for how to determine how each sens is working for you, AutoGain is an inspiration for using under- and overshoot as evidence around the anchor.6
simulation!
There is one notable problem with this idea of putting aiming scenarios in a unified vector space to sample from. It now means we can make a basically infinite number of scenarios, and it is difficult to audit if they are continuous across dimensions the way we need them to be, as well as making sure they're fair to the player. It is perfectly possible as is for it to request a drill where the targets are giga large, slowly moving, and completely exploitable by spam clicking.
Testing an adaptive trainer with only my own hand is awful and tiring. Fortunately, I can build a synthetic player, like you saw in the intro, to play through the generated scenarios, and make sure they're difficult in the way I expected them to be. It only sees the same target information a player could see and only acts by producing mouse deltas and button events. Those inputs go through the real engine, recorder, scoring, and replay path, allowing me to audit an unreasonable amount of scenarios and an individual player's curriculum in a comfortable manner.
I can also inject exploitative personalities into them. The camper parks near a high-traffic location and waits for the game to come to it. The sprayer fires at the maximum allowed cadence and hopes volume can substitute for aim.
here's a visual representation of real files from the auditing system
synthetic players on the real engine
sampled from .oar replaysfig — these are not hand-animated paths. each clip was produced by OpenAim's synthetic player driving the real engine, then sampled back out of the replay file. pick a weak hand, a stronger one, or one of the deliberately bad strategies used to attack the score.
There is one large caveat. The synthetic aimer uses many of the same motor assumptions as the model being evaluated, so while it can test the system quite well, it cannot prove that the assumed noise law describes a human, because it was born inside that law. Thus, I added small learned heads to multiple steps of the pipeline, so the loop can be closed on a human over time as data accumulates. yay big data!
elephant in the room
There is a much larger question underneath every aim trainer: does getting better here actually make you better in Valorant?
For skilled players, there is no study that lets me answer that cleanly. KovaaK's scores have shown excellent test-retest reliability, but the authors explicitly warn that an isolated trainer does not contain the cognitive load of an FPS match.7 A large meta-analysis of video-game training found almost no far transfer to general cognitive ability.8 That is farther than the claim I care about, but it is a good warning.
The most encouraging direct result I found is a small CS:GO study where novices given manually adjusted adaptive training improved in-game faster than a fixed-difficulty group.9 There were 21 participants. It supports trying adaptive difficulty, but it says very little about a player who has already spent hundreds of hours aiming. All in all, I still believe subjectively it's a worthwhile hobby, if only because I tend to find it at times more enjoyable than the game itself. Recently I've picked up Muay Thai and started fighting, and have been lifting again to keep my power up. I view aim training in much the same vein.
This endeavor may or may not make me good at Valorant. I do think aim training deserves better measurement than a scoreboard, and this is my attempt at that.
Footnotes
-
Meyer, Abrams, Kornblum, Wright & Smith (1988). “Optimality in human motor performance: Ideal control of rapid aimed movements.” Psychological Review, 95(3), 340–370. paper ↩
-
Harris & Wolpert (1998). “Signal-dependent noise determines motor planning.” Nature, 394, 780–784. paper ↩
-
Park, Lee et al. (2020). “Servo-Gaussian model” for continuous tracking. ACM ↩
-
Guadagnoli & Lee (2004). “Challenge Point: A framework for conceptualizing the effects of various practice conditions in motor learning.” Journal of Motor Behavior, 36(2). paper ↩
-
Pelánek (2016). “Applications of the Elo Rating System in Adaptive Educational Systems.” Computers & Education. paper ↩
-
Kim et al. (2020). “AutoGain: Gain Function Adaptation with Submovement Efficiency.” CHI '20. paper ↩
-
KovaaK's reliability study (2024). Test-retest ICC 0.947–0.995, with an explicit warning against treating trainer scores as in-game performance. PMC ↩
-
Sala, Tatlidil & Gobet (2018). “Video Game Training Does Not Enhance Cognitive Ability.” Psychological Bulletin, 144(2). PubMed ↩
-
Neri et al. (2021). Adaptive-difficulty CS:GO training. Frontiers in Psychology, 12:598410. paper ↩