DEV Community

hammi
hammi

Posted on

I built a two-zone gesture HUD on a raw canvas. here is the hit-test math

full disclosure first: i'm the developer of RKMovies, a 0.91 MB Android 4K media client. this is a build diary, not an ad.

most video players give you one gesture zone: drag anywhere, seek. that is simple to implement. i wanted the classic dual-zone layout instead: drag on the right half of the screen for volume, left half for brightness, double-tap to skip 10s. the catch nobody blogs about: the indicator has to appear on the opposite side of the screen from your thumb, or it hides behind your hand.

the hit-test

it is embarrassingly simple once you accept it:

zone = pointer.x < width / 2 ? BRIGHTNESS : VOLUME
delta = (pointer.y - startY) / height   // normalized, downward drag = decrease
value += zone == VOLUME ? -delta * MAX_VOLUME : -delta * MAX_BRIGHTNESS
Enter fullscreen mode Exit fullscreen mode

one idea per line. normalize by screen height, not pixels, so the gesture feels the same on a 6" phone and a tablet. invert the sign because dragging down should lower the value.

the parts that actually took time

  • dead zones at the edges. the top 48dp is the system status bar area and the bottom 48dp is the nav bar. if your gesture starts there, ignore it. 48dp because that is the minimum touch target, so fingers never land there by accident anyway.
  • indicator on the opposite side. volume drag on the right puts the indicator on the left. one boolean: indicatorX = zone == VOLUME ? 24 : width - 24 - indicatorWidth. that single line killed an entire afternoon of "why does my HUD feel wrong".
  • double-tap vs drag disambiguation. track pointerdown time and movement. if up within 250ms and under 10px moved, it is a tap: left third = back 10s, right third = forward 10s. anything else is a drag.
  • drawing on canvas, not views. one <canvas> overlay, invalidate() per frame, text drawn with the system font. zero view inflation, zero layout passes. on a 2 GB RAM phone that matters: the HUD costs us under 1 MB of RAM total.

the trade-off i accepted

discoverability is worse than a settings page. nobody finds the brightness gesture by accident. i ship a one-time hint overlay on first playback and then get out of the way. players that bolt a settings page onto every gesture lose on feel.

where to see it

the app is 0.91 MB, uses MediaCodec hardware decode so gestures never stutter, runs under 15 MB RAM, and has zero telemetry. Android APK, Windows build (NVDEC/QuickSync there), source-of-truth landing page at rkmovies.dpdns.org.

if you are building gesture UIs: normalize by screen size, put indicators opposite the thumb, and spend your time on edge dead zones, not on the math.

Top comments (0)