The live misses were palm-led strokes with the palm's lead under 0.25, tilted
0.81-0.88 toward flat, or drifting 21-22 cm up. Lead 0.3 (one hand 0.12),
edge 0.85 (one hand 0.9), drift 0.3, 4 frames off shape ridden out. The
flags: 51 strokes (49), 4 in other use (2). A refused stroke's log now says
which place rule refused it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Far stretch: both palms held out at the same far panel, then two fists
within 0.6 s, scales it round its centre (a floating window by pixels)
until a hand opens. Aims at the panel both hands' lines meet, or the
line through the palms' midpoint. Grab that holds its fire meanwhile, and
both hands are claimed from the pointer helper so a fist passing through
a pinch doesn't click or drag under a raised hand ray. Near misses log why.
- Reset button: a hand's tap shows "Reset the layout? Tap" under it for
5 s; a tap on it (or the button again) resets. A laser click resets at once.
- Lazy susan: with the other hand still or unseen, a looser shape, 18 cm
and 0.5 m/s count; any stroke rides out 2 frames off its shape; misses
log open/tilt/lead against their limits.
- Pointer helper: the two-hand gaze drag no longer logs "with the right
button" (it is the left alone).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Throws: a carry's let-go takes the fastest outward stretch in the last
150 ms before the fingers opened (a hand slows as it lets go), at 0.45 m/s
within 70 deg (was 0.55 within 60, the last 100 ms only). Every let-go
moving 0.3 m/s or more is logged.
Sent away (thrown, flicked, pushed), a panel goes along that line out as
far and as big as it was before a hand or "grab that" took it, not back
to its old spot (layouts are for that). Put down past 1.2 m, that's its
distance from then on; never taken, it flies to 3 m as before. The front
slot's screen leaves the slot the same way.
The bar's buttons sit a button's width apart, and the reset button
resets the layout on a tap (HANDS_RESET_HOLD_MS=0; 500 before).
Grab that's glow: ft_pointer's laser sits on the panel with the cursor
all the time (the 3D mouse, the gaze), so "no glow under a laser" kept
the glow off that panel. The pointer helper now tells ft-screens which
hand rays are up ("ray L R", on change and every second); our laser
counts as one only then. Controllers' lasers count as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User 2026-10-11, the second gaze test:
- "hand tracking stopped" ends were an unsigned age: ft-hands publishing just after the
loop's tnow made it negative, wrapped to 584 years, and ended held pinches, nudges,
drags and scrolls. Signed now.
- The two-hand drag mirrors the keys: hold one pinch (aim, nudge), pinch with the other:
the left button goes down where the dot is (as Meta+K with Meta+J held); the drag
lasts while either pinch holds, and the hand still pinching drags. A second pinch
during a drag joins it.
- The fast-hand filter refused dozens of still pinches (frame-to-frame palm speed is
noisy): smoothed, 1.0 m/s.
- A three-finger scroll goes on when ft-hands swaps which finger it calls pinched.
- Lazy susan: counted strokes came at 25-30 cm in 230-470 ms, at both limits: 22 cm,
quick somewhere along it (0.7 m/s peak) within 0.9 s; looser hand shape. The flags
replayed: 2 strokes in 45 flags of other use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User 2026-10-11, gaze + hands becoming the main input:
- Right-drag: an index tap on one hand, then an index pinch on the other within 1 s,
holds the right button at the gaze lock; that hand drags until it ends
(POINTER_GAZE_RIGHT_DRAG). It replaces the chord (POINTER_GAZE_CHORD=0).
- Three-finger scroll: the thumb on both the index and middle tips (3 cm) scrolls where
you looked as the hand moves, like the ray's middle-pinch stick (POINTER_GAZE_SCROLL).
- The fist guard (a hand closing into a fist: no click) now covers gaze and chord
pinches too, from each hand's own frame (not the ray's).
- A hand's laser shows only its dot: the beam starts just short of it, as the eye's
does, held still through a carry (POINTER_RAY_BEAM=0; SteamVR's beam width is one
global setting it reads only at start).
- The front slot gives a screen up to manual controls: grab that, a push, flick or
throw, or a carry by hand or laser releases it (it moves like any other and stays
where you put it); the next stroke puts it home and brings the next.
- The lazy susan counts only palm-led strokes (the return stroke does nothing; the
other hand palms the other way; HANDS_SWEEP_BACK=1 for both).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The lazy susan (the sweep, Meta+Alt+Tab) no longer turns the room: the next screen
round, by where each one lives, comes in front of you at a distance it spans at most
60 deg of the view at, and takes the typing. The next stroke sends it home and brings
the one after; pushed, flicked or thrown it goes home; turned away from for 0.6 s it
goes home; moved by hand or laser it stays. SCREEN_SPIN_BRING=0 turns the room again
(Input Settings switch too).
Sweeps were hard to make (counted strokes came at 30-35 cm, just over the 30 asked):
25 cm in 0.5 s, the hand open 1.4, tilted up to ~45 deg off its edge, and hovering near
a screen counts (the brought one sits right there). The flags replayed: one stroke in
49 flags of other use.
Grab that brought a remote display back at the 1.62 m it was resized to, 0.45 m ahead:
it filled the view and every poke went through it (both flags of the second live test).
A wide one now comes as far out as it spans at most 75 deg (HANDS_GRAB_THAT_VIEW_DEG).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User, 2026-10-11: "paw with a mostly horizontal hand back and forth like
pushing branches out of the way, left or right, and lazy susan through
windows and displays"; decided: one panel per stroke; the palm or the back
of the hand leading, never a flat hand; every stroke counts, but a large one.
An open hand on its edge (fingers open, the palm's normal within 0.55 of
level and along the motion), away from every panel, 18 cm or more in front
of the eyes, from 50 cm under them to 15 over, sweeping 30 cm sideways
within 0.6 s, drifting at most 15 cm: Event::Spin -> the spin's next (swept
left: the panel on the right comes) or prev. Not while typing, pressing,
holding, pinching or arming grab that. HANDS_SWEEP.
Tests: 40 cm in 0.4 s either way spins once; flat, slow (1.5 s), short
(15 cm) don't; back and forth spins each stroke. Flag replays: no strokes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the 2026-10-11 11:00 live test:
- The finger's line overshot on the native screens: the screen sits low,
the hand comes up steeply, and the line from a tip a few cm out met the
screen 2-4 cm ahead of the drawn fingertip (the flags' clips show the dot
there). The aim is now the tip's path: where it's heading as it comes in,
carried on to the surface, at most 1.2 times its distance off the tip's
own point; still or slow, the tip's own point (HANDS_TAP_AIM=path|finger|
straight).
- Slow deliberate pushes on the remote displays were refused (too slow,
reaching through): once the aim has frozen on the way in, a pointing
finger pushed 1 cm through the panel (on its way in: within 5 cm, 10
frames) taps at the aim, whatever its speed or angle (HANDS_LOCK_PUSH).
- A hand at a corner's zone shows the corner's glow before the tap dot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the 2026-10-11 10:49 live test:
- Taps were offset from where the user aimed, most at the edges: the tip's
own point (straight in from it) sits short of where the finger points when
it comes in at 30-50 degrees, by the tip's distance times that angle's
tangent (about 1.3 cm with the aim locked 1.5 cm out). The aim, the dot,
the cursor and the press now use the line from the index's middle joint
through its tip, met with the panel (at most 4 cm off; HANDS_FINGER_RAY).
- With a hand near, the grown buttons overlapped the screen: the bar's row
sits below the screen by the taller of the bar and a button.
- The "grab that" glow doesn't show on a panel a laser is on (the gesture
still works there).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User, 2026-10-11: after arranging a window it's almost impossible to keep it
exactly where it was: the hand moves a little as the fingers (or the whole
hand) let go. Only the let-go is treated differently, so carrying has no
added smoothing or latency:
- The panel stops following once the fingers open 8 mm past their tightest
this hold (a fist: its curl 0.3), or three times the reading's own wobble,
on two frames running, and goes back to where it was 70 ms before the
opening first showed; closed again, it follows on from there (the carry
or the two hands' stretch starts afresh). HANDS_RELEASE_FREEZE.
- A slow let-go the freeze didn't catch: back to where it was 120 ms before
(Event::Settle; HANDS_RELEASE_REWIND_MS). Throws are left alone.
- The same hand doesn't take the panel it let go of again within 250 ms.
Test: a let-go with the hand sagging 2 cm leaves the panel 1.3 mm from where
it was held (14.7 without). Flag replays: 2 pinch freezes, 2 fist ones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tapping, after the 2026-10-11 10:10 live test:
- A soft white dot on the panel where a tap would land (Guide::Touch,
HANDS_TOUCH_DOT): smaller and brighter as the fingertip comes in, lit with
a ring while pressed; it shows with the cutouts off.
- The aim locks 1.5 cm out (HANDS_TAP_LOCK): the press lands where the tip
was as it came within lockOut, not where its jitter or slide put it on
crossing; the cursor holds there too.
- A press by motion (HANDS_MOTION_PRESS): a tip that came in at 0.25 m/s or
more, 3 cm or more, and stopped or turned back within 1.5 cm of the surface
presses there (the depth reads short of the touch).
- A hand at a panel makes its round buttons at least 2.4 cm across
(HANDS_BUTTON_SIZE), and they take a fingertip or pinch 2.5 cm round
(HANDS_BUTTON_REACH, was 1.5).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the 2026-10-11 10:10 live test: pokes went down at one spot and up
10-30 cm of pixels away (the tip slides down the panel as the finger curls
back off it), so apps saw a drag. A press doesn't drag in its first 250 ms
(HANDS_TAP_MS). Two-finger scrolls started three times and ended at once:
a swipe in the air drifts off the surface; it now ends only 5 cm off, the
fingers 10 cm apart, or not two fingers for 6 frames.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the 2026-10-11 09:43 live test:
- Two fists anywhere near one panel (palms 30 cm in front to 15 behind, 30 cm
round it) resize it by their shape alone (HANDS_FIST_PAIR): ft-hands' grip
only begins on a hand seen open just before, at the panel.
- Two fingers (index and middle straight, together, level; ring and little
curled) touching the content: a swipe scrolls (a notch per 1.2 cm, the
content following), a lift unmoved clicks, held still they act as a poke
(HANDS_TWO_FINGER_SCROLL).
- Stuck presses: a press isn't held through a tracking loss (only grabs are);
a poke lets go as the index curls; a pinch-click as the tips are seen 4.5 cm
apart; either held still 1.5 s (HANDS_PRESS_STILL_MS).
- The hand ray hides within 22 cm in front of a panel and for 0.8 s after
(HANDS_RAY_HIDE_REACH); the pointer helper ignores a ray-less pinch there
without the gaze (it woke the laser).
- The push/flick hint glow is off (HANDS_PUSH_GLOW): it covered near panels
the palm faced while clicking.
- Grab that gives panels that look small up to 10 deg more aim margin
(HANDS_GRAB_THAT_SMALL).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the 2026-10-11 09:25 live test:
- A fist only resizes (HANDS_FIST_RESIZE_ONLY, default on): one fist holds the
panel still as an anchor; two hands with a fist among them scale it about
its centre, no move or turn. Pinches still carry and turn.
- Throws need 0.55 m/s (was 1.0): the user's releases were 0.62-0.96 m/s.
- Grab that brings a panel back at the width it had when it was last sent
away from arm's reach (g_nearWidth), not grab that's maxWidth.
- A natural reach arms grab that: palmArm 0.2 (was 0.45), aimFacing 80 deg
(was 55), aim margin 10 deg (was 6). Flag replays: 11 fires (was 8).
- Curve: a curved screen's radius follows its distance from the head after
every move, and it bends at most SCREEN_CURVE_MAX_DEG (100) round: one
curved up close and thrown 3 m out at full width had wrapped into a
cylinder.
- The pointer helper releases its laser while a hand touches a panel
(ft-screens' near claim): the laser from the head to the fingertip's
cursor got in the way and held the button, refusing pokes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two fists (flags 215945 and 215955, the live log): the right hand's fist began as a
pinch with the thumb on the index while the other fingers were still closing, waited
0.6 s on "grab that" (armed by the open hand held out to the near panel), then clicked
the content and held the click 9 s, so the left fist's hold never got a second anchor.
- A second hand at a panel the other holds (15 cm in front, 12 behind, 10 round) joins
it with a pinch or fist anywhere on it: the stretch (move, scale, turn). No click,
poke, push or flick on a held panel (HANDS_JOIN_REACH).
- A pinch on the content by a closing hand waits up to 150 ms to become a fist; a wait
on grab that ends as a join, a fist or a pinch, as the hand is then.
- Fists reach 11 cm in front and 10 behind; a curled hand is claimed that far
(HANDS_FIST_REACH).
- Controls: the nearest wins (the bar took the curve button's pokes), round buttons get
a 1.5 cm hit area, a pinch presses them (HANDS_BUTTON_REACH, HANDS_PINCH_BUTTONS); the
log names the control.
- Guides: per hand, what its soft glow shows (corner, edge, button; lit while held).
- A carried panel turns at most 6 deg a tick about the hand's point.
- Grab that: the line through the palm has to meet the panel (no margin, no head cone),
panels within 0.8 m aren't taken, the arming hold faces the aim within 55 deg and is
still under 0.5 m/s, and a send-away quiets it for 1 s.
Tests: buttons, guides, the fist settle and reach, joins, two-fist flag replays (before:
a click and no two-hand hold; now: 9.4 s of stretch with the panel 4 cm nearer or 8
cm farther), grab that at near and far panels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fh_smooth.h's Params get maxCutoff (0, the default: no limit, as before for the ray and the
pointer helper). With the tracking gate on, a carried panel's turn (the wrist turn, a card's room
turn) is smoothed at most at 4 Hz however fast the hand's frame turns, so a flip the gate takes
after three agreeing frames is followed over about 40 ms instead of in one tick. On the third live
test's flags, carried panels' turn per 90 Hz tick: second difference rms 1.87 -> 1.60 deg, 99th
percentile 6.2 -> 5.2, the centre's 11.5 -> 9.7 mm (hands-throw: 33.3 deg and 159 mm).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the 2026-10-10 19:28 session (ten flags, ft-screens' and the helper's logs):
- Tracking gates (Params::gate, HANDS_TRACK_GATE): a held hand's frame that no hand makes
(faster than 4 m/s, or more than 0.5*100 m/s^2*dt^2 + 2 cm off where its velocity had it) is
dropped unless three in a row agree; its frame turning faster than 540 deg/s likewise. One
camera's frames and frames near the edge of view (50-80 deg off the head's forward) are smoothed
harder and don't turn the panel; past 80 deg the hand counts as unseen (the panel holds). On the
side-grab flag (203635) the left hand read alternately at two places 10-20 cm apart with its frame
flipping 100-180 deg.
- CarryPose faces the head from a point between the hand and the panel's centre when the panel is
held near the eyes by a far point (kCarryShare): facing from the centre had no steady answer
there and spun 0.6 m panels held by an edge at arm's reach. A centre near the eyes stops turning.
- A pinch by a closed hand (index <= 1.15, the others <= 1.05) is a fist grab, not a pinch click:
ft-hands called the user's two fists pinches (flag 203215) and the content was clicked.
- Two hands turn a panel on every axis with the line between them and roll it with their wrists
(StretchTurn, Params::stretchTurn, HANDS_STRETCH_TURN); fists find a panel by its edge too.
- A pinch the touch zone claimed takes the border 3 cm past the band's depth (Params::pinchSlack).
- The push (Params::push, HANDS_PUSH): an open hand, palm to the panel, pushed straight into it
at 0.5 m/s (5 cm in) or 15 cm in slowly, throws it 3 m out like a flick. A flat or pushing open
hand doesn't poke. throwAims() says which panel a push or flick would throw.
- The palm menu: facing 45 deg (60 to leave), the palm still, not at a panel (Input::atPanel,
HandAtPanel) nor for a second after it or after a hold; a pinch with the middle finger in it
does nothing on the ring.
- "Grab that": Aim() for the target's glow.
Tests: gates (alternating misreads, real moves, a real jump, the edge of view, flipping frames,
one camera), fist pinches and two-fist stretch and turn, StretchTurn, push vs poke, rest, wave and
the back of the hand, pinch slack, the stricter palm menu; replays: --carries (steadiness of
carried panels on the flags, gates on and off), --grabthat with pushes and the palm menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A hand pinching the resize handle after it moved to another corner
(a laser or the gaze took it there) resized from the bottom right:
handinput read the tab as that corner always. It takes the corner
from where the handle is now.
- A floating window's last resize can be answered after the release:
its opposite corner stays kept for half a second after a resize ends,
not reset to the top left at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User, 2026-10-10. Both send the panel 3 m from the eyes
(HANDS_THROW_DISTANCE), facing them, over 400 ms easing out; "grab that"
brings it back from there.
- Flick (HANDS_FLICK): a hand at a panel (its palm within 12 cm in
front of it or 4 cm behind, over it, and nearly still there: under
0.6 m/s) flicks out: away from the eyes (within 40 deg) at 1.2 m/s or
more (HANDS_FLICK_SPEED, a fit over 100 ms), 12 cm or more from there
within 200 ms, the fingers open (mean extension 1.5 or more) and
opening on the way by 0.15 (HANDS_FLICK_OPENING; 0 lets an open hand
pushed out flick too). The panel goes out along the line from the
eyes through its centre. Not a poke (a pointing hand's fingers read
about 1.3; an open hand coming through a panel faster than 0.8 m/s
doesn't poke it), not a wave (the direction), not "grab that" (its
pull back is toward the eyes). Without the opening rule the
recordings' probe moves, with a panel 0.3-0.45 m ahead, flicked 6
times in 11 minutes; with it once. None in the 13 "grab that" flags.
- Card throw (HANDS_THROW): a carry by one hand (a card, a fist, a pinch
on the border, bar or title; not a resize or two hands) let go moving
at 1.0 m/s or more (HANDS_THROW_SPEED; a fit over the 100 ms before
the fingers opened), within 60 deg of away from the eyes: the panel
goes on along that line to 3 m from the eyes. A slower release leaves
it where it is. The velocity's track starts afresh when the hand comes
back from a gap, and a flick doesn't reach back over one.
- vr.cpp animates a throw like a "grab that" move (ease-out), and
forgets that the panel came by "grab that": the throw is its place
now, so the gesture brings it back and sends it back there.
- "grab that" finds a panel along the palm's line within 3 deg seen
from the eyes (or 10 cm), so a 0.6 m panel 3 m out isn't missed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User, 2026-10-10: Vision Pro-style resize from all four corners. Only the
bottom right worked by laser (the .resize handle), and a resize kept the
screen centred.
- One resize handle per panel still (SteamVR's 128 overlays are tight:
a third remote display once didn't fit), but it moves to the corner
being aimed at (vr.cpp UpdateCorner): a laser's ray (a controller's,
or the pointer helper's device, which the gaze and the hand ray
drive) landing within a fifth of the screen's height (5-20 cm) of a
corner, or a hand's pinch at one (handinput's corners()). Its texture
turns for that corner, it brightens the closer the aim (from the
controls' idle 0.55 to 1), and it's lit blue while resizing. With no
aim it lingers 0.4 s, then goes back to the bottom right.
- Any corner resizes and the opposite one stays put: screens and
remote displays keep their aspect (the corner follows the diagonal,
ResizeFromCorner, on the cylinder for curved ones: SurfacePose),
floating windows take the pixels from that corner (ft-floatd's new
size is placed keeping the opposite corner, keepX/keepY; every corner
of a window resizes now, the title bar's ends included).
- Near hands: a pinch at any corner resizes the same way (the carry
now gives the pose as well as the width).
- Gaze pinches (gazehand.h): with the pointer on a screen's resize
handle or grab bar, the pinch is a press after 0.12 s still
(POINTER_GAZE_CONTROL_HOLD), not 0.5: nothing to click there, and the
hand drags the resize. The chrome overlays are announced to the
helper as they're made, so its ray and the gaze find a new remote
display's handle at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User, 2026-10-10: the card hold (the panel rigidly with the hand) should
start only when the edge is held with at least the index and middle
fingers together against the thumb, or all four fingertips bunched at
it. A plain thumb-index pinch at an edge stays the border's move (or a
corner's resize).
The fingers come from the keypoints (ft-hands' pinchD[1] is the fit's
only): thumb to middle tip within 4.5 cm and index to middle tip within
3.5 cm, or every fingertip within 5 cm of the thumb tip. The straddle
rule (thumb and index tips either side of the plane at the border) is
as before. The middle finger often closes after the index: a border
move begun straddling the edge becomes a card, from where it is, if the
fingers join within 300 ms. The green glow shows only for the card's
fingers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Losing the hand for 250 ms (or ft-hands ending its pinch as lost) dropped
whatever it held. Now a pinch, card or fist grab, and a poke's or a
pinch's press in KWin, last until a clear release: the hand seen with
its gesture up and its fingers open (thumb to index tip over 3.5 cm; a
fist's fingers over 1.35 extended) on 2 hands frames in a row, or the
gesture up for 6 frames whatever the fingers say. A pinch that
flickers up for a frame or two in fast motion, or that ft-hands ended
as lost while the hand comes back still pinched, holds on. Unseen for
1.75 s (HANDS_HOLD_LOST_MS), it ends where it is.
While unseen (or on a frame whose point jumped more than 12 cm, up to
2 in a row: a glitch), the carried point coasts on the hand's last
velocity (a fit over 110 ms), slowing to a stop over 150 ms. When the
hand is back, the smoothing restarts there moving at the gap's average
velocity (fh_smooth Point::Seed, new) and the panel blends from the
coast to it (smoothstep over 100 ms, longer for more than 0.6 m/s on
average). The raw track starts afresh, so a throw's release velocity
never sees the gap.
Synthetic carries at 0.6 m/s losing the hand for 3, 6 and 10 frames:
held, the panel's largest step 12-17 mm a tick (6.7 moving; 13-24 with
3 mm of noise), back under the hand after. Two glitch frames 30 cm off:
11 mm. Release now comes 2 frames (67 ms) after the fingers open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The second live test (2026-10-10 18:20) refused 39 of 50 pokes. Fixes:
- "another fingertip nearer the panel than the index" (14): the replays
show the tracker reading a pointing hand's middle finger nearly as
straight as the index (1.65-1.75 against 1.7-1.8) with its tip -7 to
+4 mm from the index tip's depth at 20-30 deg. The other tips may now
be up to 6 mm deeper than the index (it had to be 5 mm nearer). Made-up
pokes along the finger in the replays press 19 of 23 (probe-moves, 3
before), 31 of 55 (lit, 18), 4 of 6 (2026-09-29, 1); a flat hand with
the middle tip 1.2 cm deeper still doesn't press.
- "off the content" (25): most were the index crossing the plane below
the panel as the hand reached for the bar (each was followed by a bar
grab). Those aren't poke attempts any more (counted as offContent and
logged once as "not a poke"); a tap within 1.5 cm past the content's
edge presses at the edge (the desktop's panel is on the bottom edge).
- "past the surface for 1 frame" (5; 1.7 and 2.0 cm in on the content):
a one-frame press needs 1.7 cm of travel (2.5 before); under 2.5 cm
the tip must come in on each of the last 3 frames. A finger held 1.5
cm out with 6 mm of noise presses 4 a minute (2 with the old rule, 0
with 3 mm of noise), at 2.5 cm none.
- Refusal logs say where the tip crossed and, for "not nearest", which
tip, how deep, and the fingers' extensions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The near report to the pointer helper ("near <ns> <L> <R>") was one flag
for two things: the hand's ray hides, and its pinches and grips are
ft-screens'. With the 0.075 m zone, its 2.5 cm leave margin and 200 ms
linger (there so the ray doesn't flicker at the edge) would still have
swallowed pinches made just outside the zone, the live test's complaint.
Now ft-screens says 1 when the hand is within the zone itself (or
pressing, grabbing, or waiting on "grab that"): its gestures are ours; 2
in the margin: the ray stays hidden, the gestures are the helper's (ray or
gaze); 0 otherwise. The helper takes 1 as the claim and 1 or 2 as near for
the ray; an older helper takes 2 as 1, as before. ft-screens' own pinch
and fist begins need the zone itself too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the live test of 2026-10-10 (handinput.h has the rules and numbers):
- The touch zone (HANDS_NEAR_REACH) is 0.075 m (was 0.30: "1 foot is way
too far, it should be closer to 3 inches"). A hand stays near until
2.5 cm farther out, then 200 ms; a hand resting low by the body is near
only within 4 cm. Beyond the zone a pinch is the ray's or the gaze's.
ft-hands is asked for 30 Hz while a hand is within 0.30 m of a panel, so
a tap from outside the zone isn't seen at a still hand's 15 Hz.
- Pokes: a decisive tap (2.5 cm in on average over the last 300 ms, on its
way in on the frame before too, 0.10 m/s or faster) presses on its first
frame past the surface; others still need two. The finger's angle is
taken knuckle to tip (the 2 cm last bone turned 15-20 degrees with a few
mm of noise), the index counts as straight from 1.5 (was 1.6), a new
press needs the tip back 1 cm out (a noisy pull-back pressed twice), and
a finger past the surface more than 5 frames or 4 cm deep is reaching
through. On noisy synthetic taps (3-6 mm of hand noise) 50-56 of 60
shallow taps (0.5 cm past) press, against 34-36 with the old rules; a
still finger 1.5-2.5 cm out presses no more often than before.
- Logs: each press and release (panel, place, depth, speed, frames), each
poke attempt that crossed the surface without a press with the rules
that held it back, each grab and its end, printed by ft-screens as
"hands: ...". handinput state counts them.
- Pinch grabs test where the thumb and index meet (the gestures file's
point is the knuckles, 5-8 cm behind them, which the 3 cm deep zones
rarely had): a band 6 cm outside to 2.5 cm inside the border, 4 cm deep
either side (HANDS_GRAB_BAND); corners (6 cm) resize, the rest moves.
The glow shows it before the pinch: amber at a corner, blue on the
border, green for a card pinch.
- A near pinch on the content clicks where the fingertip's cursor was (or
where the fingers meet), held while the pinch is (HANDS_PINCH_CLICK).
- Card pinch (HANDS_CARD_PINCH): the thumb and index either side of the
panel's plane at its border, the hand along it; the panel goes rigidly
with the hand, position and full turn, past 1 cm and 3 degrees.
- Fist grab (HANDS_FIST_GRAB): a grip begun with the palm or knuckles at a
panel carries it and turns it with the wrist; two fists stretch it. A
fist or pinch on a hand "grab that" has waits until it fires or lets the
hand go (at most 0.6 s): on the user's flag, with a panel where the
gesture brings one, a pinch-click on the closing hand stole the second
fire before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflict: docs/reference.md's hands settings line (typing's POINTER_TYPING_*
keys next to reach's HANDS_NEAR_REACH, HANDS_GRAB_ROTATE* and
HANDS_GRAB_THAT*): both kept.
Typing and grab that: a hand typing holds back (the helper's "typing" word)
doesn't arm or fire "grab that" either, so it never claims a fist as near;
vr.cpp's UpdateGrabThat leaves it out, as Poke and Grab already do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While one hand carries a panel, the hand's turn since the grab turns it
too, 1:1 about yaw, pitch and roll (user, 2026-10-10: line displays up).
The turn comes from the wrist and the index and little knuckles, which
hold still while the fingers pinch, smoothed with fh_smooth's Direction
filter, relative to facing the head across the floor from the hand, and
is applied on top of the panel facing the head: carrying a panel round
you still only keeps it facing you. Nothing turns until the hand has
turned HANDS_GRAB_ROTATE_DEADZONE (5 deg), then it turns from there
without a jump; within 2.5 deg of level it snaps level. Let go, it keeps
the turn, and the next grab starts from it. Corner and tab grabs only
resize; a stretch is unchanged and the hand left holding starts afresh.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ft-screens takes the pointer helper's "typing <L> <R>" (lapses after 3 s):
such a hand presses nothing, grabs nothing, and the cursor doesn't follow its
fingertip; a press or grab already held carries on. handinput's state counts
them as held back (typing).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The near claim reaches 0.30 m (was 0.15): within it the pointer helper
hides that hand's ray and leaves its pinches alone, and the index pokes
(user, 2026-10-10: "near = poke within a foot"). A hand near a panel
stays near until 5 cm farther out, then 250 ms on, so the ray doesn't
flicker at the zone's edge. A hand resting low by the body (the poke's
resting rule) is near only within 0.15 m, so a low panel within reach
doesn't take gaze pinches from the lap. The poke's rules are unchanged:
on the PR #3 desk and touch replays the presses are the same as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A near grab carried the pinch point moved ahead along the hand's velocity, which is mostly
noise times the lead, so the panel shook. It now carries the pinch point as the cameras saw
it, smoothed (fh_smooth.h, read every frame ft-screens draws), and moves nothing until the
point is 1 cm from where the pinch began, then carries on from there without a jump. The
head the panel turns to face is smoothed too. On the probe-moves replay the carried point's
second difference per 90 Hz tick went from 13-17 mm rms to 2.3-2.6 mm; it now trails the
capture by 85 ms while moving.
Settings: HANDS_GRAB_SMOOTH_MINCUTOFF, HANDS_GRAB_SMOOTH_BETA, HANDS_GRAB_DEADZONE.
handinput_test --replay reports the grab input's smoothness.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User decision (2026-10-08, "approach from front"): a poke counts only when the fingertip moves
into the panel (4 cm/s or more over the last 100 ms when it crosses the surface) with the
finger's last bone within 50 degrees of the panel's normal (HANDS_POKE_ANGLE), and not from a
hand resting low by the body: palm over 0.30 m under the eyes and within 0.32 m of the head
across the floor, until it's 5 cm higher or further out (HANDS_POKE_REST_BELOW, _NEAR; the
hand recorder's desk take had typing palms at 0.34-0.40 m under, 0.23-0.28 m out). The slow
1 cm-deep press is gone. The index counts as the poking finger when it's nearest the surface
or when the hand points (index straighter than the others by 0.2): pointing at a panel from
below, the loosely curled fingertips sit 1-3 cm nearer it (the touch take).
ft-handreplay --publish-dump writes FHDUMP02 (each set's dequeue time too), and the replay
tests read a take's poses.jsonl and prompts.jsonl next to the dump: head poses, panels around
the head, the touch take's dots, made-up pokes from still pointing hands, low panels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- handcut: reads fh_hands version 2 (the skeleton's joints, radii, elbow, pinch point; version 1
still works) and the gestures file; the matte is hand-shaped: a tapered capsule per finger
bone, the palm and the thumb's web as convex slabs, the forearm toward the elbow, drawn with
a soft-edged convex polygon shader; its margin is an angle from the eyes plus motion
(HANDS_MATTE_MARGIN, _MOTION, _MOTION_MAX), the capsules stay selectable (HANDS_MATTE), and a
per-distance correction hook is off by default (HANDS_MATTE_SHIFT). The CPU part is in
handcut.cpp (no GPU or SteamVR calls), the renderer in handrender.cpp.
- handinput: the near claim, poke to click (index straight and nearest, two frames at the
surface, an approach speed or 1 cm deep, release 1.5 cm out or 1 cm back, the click held
still until a 1.5 cm slide, re-press guard, through-the-panel release), pokes on the curve,
reset, dock and close buttons, pinch on the bar, edges, corners, tab or title bar to move
(facing the head) or resize, two hands to stretch and turn, two hands to one, lost hands.
- vr.cpp: hands read once per tick, "hands <L> <R> <allowed>" from the pointer helper, "near
<capture_ns> <L> <R>" to it per hands frame, want-hz while near, glows on the cutout buffer,
hand holders for KWin's button and carried panels, the handinput and cutouts commands.
- tests/handinput_test.cpp: unit tests, and replays of ft-handreplay --publish-dump
(scripts/test-handinput.sh, justfile test-c).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>