← Module 9/Game feel
RU
Module 9 · Design (theory)

Game feel

The invisible engineering that engineers chronically underrate. The timing math is native MathML (no libraries).
write-up~16 min🏠 lab
The gist in 20 seconds
Game feel — the moment-to-moment tactile satisfaction — is mostly invisible engineering: input responsiveness plus a stack of tiny "juice" tricks (coyote time, jump buffering, hit-stop, screen shake, squash-and-stretch, particles). None of it appears in the game's "rules" — it is the difference between a dead jump and a live one. Engineers underrate it, and it is exactly what often separates an indie hit from a tech demo.

Context

The term was fixed by Steve Swink in his book "Game Feel" (2008): the sensation of controlling a virtual object in real time — something that cannot be reduced to rules but that the player feels in their hands. The canonical examples are Celeste and Mario: their jumps feel "right" not because of the movement formulas but because of dozens of small things tuned around them. The motto of the craft is "juice it or lose it": without a feedback layer a technically correct mechanic reads as dead.

The key idea an engineer has to accept: what the simulation does and what the player perceives are two different things, and game feel lives precisely in the gap between them.

The mechanism

Game feel is assembled from two families of techniques: input forgiveness and feedback.

Input forgiveness — fair controls without explanation

Neither technique is explained to the player — they simply feel that "the controls are fair". That is the illusion of fairness: a concession disguised as responsiveness.

Feedback — selling "weight" and the event

The common denominator of the whole stack is separating "what the simulation does" from "what the player perceives". The simulation stays honest and deterministic; the feel layer lives on top of it and lies exactly as much as the sensation requires.

The math of timing

Why a "forgiveness window" is needed at all comes down to frame arithmetic. The budget of one frame at 60 fps:

tframe=160 s≈16.67 ms

Human reaction time is on the order of 200 ms. In frames that is:

200 ms16.67 ms / frame≈12 frames

So about a dozen frames pass between "I see the edge" and "my thumb pressed" — which is why a forgiveness window of around ~10+ frames feels not like a cheat but like compensation for the player's physiological delay.

The second basic feel formula is camera smoothing (lerping toward the target every frame):

pnew=p+(target−p)·k

Convenient, but tied to frame rate: the same k at 30 and at 144 Hz gives a different catch-up speed. The frame-rate-independent variant makes the coefficient a function of dt:

pnew=p+(target−p)·(1−e−k·dt)

Why exactly that is in the deep end on the math.

Jump timeline: the forgiveness windows left the edge landing coyote ~6 frames after buffer ~5 frames before a "late" press an "early" press both presses miss the frame — and the jump fires anyway

🕹 Games to play — and what to notice

Game feel cannot be read — you catch it with your fingers. Four platformers, from a single basic knob to a whole stack of sensations; for each, what is tuned and what to notice hands-on (simple to complex).

Super Mario Bros. 1985 · variable jump · the foundation

Where it all started. Jump height depends on how long you hold the button (hold and you go higher, release early and the upward velocity is cut) plus inertia: Mario neither starts nor stops instantly. One or two knobs, but they are what makes the controls tasty — without them the jump is dead, at a fixed height.

🎮 Play: any Mario (SMB or Odyssey). Make two jumps in a row — a short tap and a held press: the height differs (variable jump). Run and turn sharply — Mario slides on inertia rather than snapping to zero. Those are the two most basic feel knobs.

Celeste 2018 · the forgiveness stack · canonical

The benchmark of modern game feel, documented by the developer herself (Maddy Thorson). The "forgiveness" stack: coyote time (a jump ~0.1 s / ≈6 frames after leaving the edge), jump buffering (a press before landing fires on the frame of contact) and corner correction (bump your head on a corner and the game nudges you sideways past it; clip a corner with a dash and it lifts you onto the ledge). All of it fudges in the player's favor just enough to feel responsive rather than magnetic.

🎮 Play: Celeste (or the demo). Jump off an edge deliberately late — coyote time saves you. Jump right against a low ceiling in a corner — notice how you get nudged past the corner (corner correction) instead of bonking and falling. Turn on Assist Mode and you will see the game already forgives; assist only amplifies it.

Super Meat Boy 2010 · precision · feel under pressure

The opposite pole: minimum concessions, maximum responsiveness. The controls are so direct and grippy (fast acceleration, sticky walls) that a death always reads as "my fault". The key is the instant respawn: zero loading between attempts, so the feel is present on every one of hundreds of deaths rather than once a minute.

🎮 Play: Super Meat Boy (demo or full). Die 20 times in a row on one screen and notice that there is no pause between death and the next attempt — that is part of the feel, not a UX detail. Press against a wall — instant grippy contact and a bounce, with no mushy lag.

Hollow Knight 2017 · combat feel, not only jumping

Feel extends beyond movement into combat. A nail strike gives hit-stop (a micro-freeze on contact → "mass met mass") and knockback (the recoil pushes you too), while striking downward onto an enemy or a spike gives a pogo: a bounce upward that turns combat into platforming. Plus screen shake and particles. The same stack, applied to fighting.

🎮 Play: Hollow Knight (demo). Hit an enemy point-blank — feel the micro-pause of the strike (hit-stop) and the recoil. Jump onto an enemy or spikes and strike downward — you will bounce (pogo); try hopping along a chain of spikes on your nail. Feel is not only about jumping.

Deep end · design: why forgiveness works and Disney's 12 principlesskippable

Perceptual fairness: a ~200 ms reaction time

The player is not controlling in real time — they are controlling with the lag of their own nervous system (~200 ms, ~12 frames at 60 fps). Without forgiveness every "but I pressed it!" is true: the intent was on time, the motor response was late. Coyote time and jump buffering close the gap between perceived (when the player decided) and actual (when the input arrived). Hence the illusion of fairness: the game feels responsive precisely because it secretly plays along — but within the bounds of human delay, no more, otherwise it feels magnetic.

Disney's 12 principles in real time

Classical animation is a ready-made vocabulary of feel techniques translated into interactivity:

  • Squash & stretch — deformation of mass under acceleration and landing; sells springiness and weight.
  • Anticipation — a micro wind-up before an action (a crouch before a jump) — the eye gets time to "read" the intent.
  • Follow-through & overlap — limbs and cloaks catching up with the body after it stops; removes the "robot".

The difference from film: in cinema the animator owns the timing, in a game the player dictates it — so the principles become procedural (squash as a function of vertical velocity, anticipation as a couple of frames before the start).

The juice stack as a checklist

A practical habit: take the raw mechanic and run it down the list — input forgiveness (coyote, buffer), hit feedback (hit-stop, shake), motion (squash, anticipation, follow-through), ambient (particles, camera lerp/lookahead, sound). Each item is added behind its own toggle so you can turn it off and feel its contribution (which is exactly what Lab 09 does).

Deep end · math: frames↔ms and geometric camera dampingskippable

Frames ↔ milliseconds

All feel timings are easier to keep in frames but should be measured in milliseconds (otherwise "6 frames" at 144 Hz is a different physical duration). The conversion:

nframes=tmstframe,tframe≈16.67 ms (60 fps)

A forgiveness window of ~10 frames ≈ 167 ms — less than reaction time (~200 ms), so it compensates for motor delay without tipping into cheating. Hit-stop is specified as N frames (typically 2–6): the gameplay tick freezes for N frames and the strike reads as a collision of masses. Too large an N and the game feels sticky; too small and there is no effect.

Lerp as geometric damping

The naive per-frame lerp p ← p + (target − p)·k leaves an error after m frames of:

em=(target−p0)·(1−k)m

This is a geometric progression: the error falls as (1−k)m. But m counts frames, not time. At 144 Hz there are ~2.4× more frames in the same second than at 60 Hz, so (1−k) is applied ~2.4× more often — the camera catches up noticeably faster. The same k → different feel at 30/60/144 Hz. That is the bug in the naive lerp.

The frame-rate-independent form

The fix is moving to a continuous model: damping is an exponential of time rather than of the number of steps. Substituting the real dt:

e(t)=e0·e−k·t⇒k'=1−e−k·dt

Now the per-step coefficient depends on dt, and over the same duration the error falls identically at any frame rate. The same trick applies to any exponential smoothing (velocity damping, fades), not just cameras.

Deep end · engineering: juice without losing determinismskippable

Feel breaks a naive game loop more than you would expect. What to keep in mind:

  • Hit-stop freezes gameplay, not the UI. The freeze should stop the physics tick and the simulation but not the presentation layer: menus, the cursor and sometimes the particles themselves keep living. If you freeze the global timescale everything stops, the interface included — which looks like a freeze or a bug. Separate feel effects from the simulation tick.
  • Determinism. Coyote, buffer and hit-stop change the timing of inputs, and in a networked or replay game that is part of the state. Count the windows in frames of the fixed tick (not in real seconds) and store the counters in simulation state — otherwise replays and netcode drift apart. Keep screen shake and particles purely on the presentation side: they must not affect the simulation.
  • Frame-rate-independent timing. All feel constants live in milliseconds/seconds and get multiplied by dt in the step (see the deep end on the math). Then game feel is identical at 30/60/144 Hz. The exception is a fixed physics tick: there the frame is stable and windows can be kept directly in tick frames.
  • An A/B juice toggle (Lab 09). Architecturally it pays to put all the juice behind one flag or layer so it can be switched on and off without touching gameplay. That is both a test bench (feeling each contribution) and determinism insurance: if the toggle changes the outcome of the simulation, juice has leaked into the logic, and that is a bug.
Analogy
Game feel is the difference between a car with power steering and suspension and one without. Same engine, same route, same physics — but driving it is a different thing entirely. Coyote time is the play in the steering that forgives an imprecise movement; hit-stop is the recoil you feel in your body; squash-stretch and particles are the suspension working every bump. Take it all away and you will still drive, but your hands will remember a dead car.
Why it matters
This is the most "non-engineering" topic in the craft — and the highest-leverage one for the goal of understanding what game developers actually need. An AI-for-games engineer with no sense of game feel will build something technically correct and dead: the simulation is right and it is unplayable. And it is a rare place where AI/ML is almost never the answer — feel is hand-picked constants and craft, not a learnable function. A good reminder that ML is a tool inside someone else's craft rather than its center.
🏠 Lab — feeling game feel
An interactive lab with no code: a playable platformer right in the browser (on a phone, with on-screen buttons). Drag the coyote-time slider, toggle jump buffer / hit-stop / screen shake / squash on and off — and feel the difference on the same mechanic. Open the lab →
The timing math (frames↔reaction, lerp's dependence on FPS) is in the deep-end tab above; to feel it live, use the "🕹" and "🔧" sections below.
🔧 Run it and poke at it — on your home machine
What to play and what to notice is above (🕹). Here — for those who want to turn the feel knobs themselves:
🔧 Tinker (debug) ~40 min, PICO-8 / Godot
Take an open-source platformer — "Celeste Classic" (the PICO-8 original, source available) or any Godot template. Find the coyote-time constant and set it to 0, then replay the same stretch: the controls immediately go "deaf". Put 6 frames back and it comes alive. Do the same with hit-stop and squash off. You are not writing the system — you are turning someone else's knobs and catching what each contributes.
🧪 Test (through a QA lens) ~15 min
Play as a responsiveness tester: at every edge ask "was that jump fair?"; eyeball the input lag; look for places where the buffer or coyote time do not fire (double jump, against a wall, on a moving platform) — the typical holes in feel.
Checklist: zeroed coyote time in the code and felt the "deafness"; put it back and it came alive; found at least one case where forgiveness does not work. The canonical talk on what juice contributes is "Juice it or lose it" (Jonasson & Purho).
🔁 Beyond games — where this transfers
The essence of game feel is separating "what the system does" from "what the human perceives" and engineering the perception specifically. That is all of UX engineering:

Frontend / UX: optimistic UI (showing the result before the server answers = the same jump buffer/coyote time), skeleton screens, perceived performance, input forgiveness in forms.

ML / AI: streaming LLM output (tokens as they are generated — perceived latency drops several-fold at the same real latency); "thinking…" indicators; all the UX wrapping around slow models is game feel for an AI product.

Distributed / networking: latency hiding, prefetching, speculative execution — the same "perceived ≠ actual".

Principle: perceived ≠ actual; engineer the perception, not only the system — and compensate for human delay without tipping into "magnetism".

Connections
overlap
Classical vs ML — game feel is hand-picked constants and craft rather than learning; a vivid example of "AI is not needed here" and of de-centring ML.
overlap
ECS / data-oriented design — feel lives inside the frame budget and tick timing: hit-stop and frame-rate-independent smoothing are about that same 16.67 ms and fixed step.
overlap
Doom / BSP — Doom's rendering speed directly produced the sensation: a high, stable frame rate is game feel too, before any juice at all.
Questions worth asking
Why does coyote time feel "fair" when it is technically a concession to the player?
Because it compensates for a real physiological delay. The player "decides" to jump when they see the edge, and the input arrives ~200 ms (≈12 frames) later — the motor response is late, not the intent. A forgiveness window of ~6 frames gives back the time their own nervous system ate. Subjectively it is not "they gave me something extra" but "the controls are finally responsive". Fairness here is about perceived and actual lining up, not about the literal rule.
Why does hit-stop sell "weight"?
The brain reads the pause as a collision of masses. In reality heavy objects momentarily "stick" on impact and lose speed; a short freeze (2–6 frames) imitates that micro-moment of inertia. Without it a hit is instantaneous and bodiless — as if there were no energy. With it the strike gains physical legibility: the eye has time to register contact and the body fills in the sense of force. Cheap to implement, huge in effect.
Can feel be A/B tested?
Yes — that is exactly Lab 09 (juice on/off behind one toggle). But be careful with the metric: "feel" is poorly captured quantitatively. Completion time or retention may not differ even though the juiced version is subjectively far more alive — because you are measuring the wrong thing. Qualitative signals work better (what players say, where they look, where they quit), plus a direct on/off comparison on yourself. A/B is useful for catching that you accidentally made things worse, not for "optimizing feel" against a single number.
Can feel be learned by a machine?
Essentially no, and it is an excellent example of "AI is not needed here". Feel is a handful of hand-picked constants (how many frames of coyote time, how much hit-stop, what k for the camera) plus the artistic judgment of "this feels alive". There is neither a dataset nor an objective function you could honestly optimize: "satisfaction from a jump" is not differentiable. ML can help at the edges (suggesting particles, generating an asset), but the tuning itself is done by a human by feel. A topic where craft beats learning.
Why do engineers underrate this?
Because feel is invisible, not written into the "rules" and hard to measure. It does not appear in the mechanic's spec: "jump = an upward impulse" is formally complete and plays dead. Engineering culture values what can be specified and tested with a number, while feel is a subjective "this feels right" that does not pass code review. It is also diffuse: not one feature but twenty small things, each individually "insignificant". The result is that the layer which makes or breaks a game systematically falls outside the engineering field of view.
Further reading