Skip to content

Stylus on tablet: strokes break up, and pencil pressure is ignored #338

Open

Description

@difficcd

Reported from Telegram (2026-09-23), on a tablet.

Two symptoms, filed together because they share the stylus input path and may share a cause:

  1. The line breaks up — a stroke comes out in pieces instead of one continuous line.
  2. Pencil pressure does nothing — width does not follow how hard the pen is pressed.

What is already ruled out

  • setPointerCapture is set on pointer down (hooks/useGesture.ts:51), so losing the pointer off
    the element is not it.
  • touch-action: none is on the canvas and its wrapper (ui/CanvasStage.tsx:73,91), so the
    browser's own scroll/zoom gesture is not stealing the move events.

Leads for the pressure half

src/App.tsx:1133 and :1146:

const pressure = pressureOn && e.pressure > 0 ? e.pressure : 0.5;
pen: pressureOn && e.pointerType === 'pen',
  • If the tablet reports the pencil as pointerType === 'touch' rather than 'pen' — some
    Android stylus stacks do — then pen is false. The renderer then falls back to the heuristic at
    canvas/strokes.ts:411, which decides there is pressure only if some point differs from 0.5.
  • Touch pointers commonly report pressure as a constant 1, or 0. A constant is not 0.5, so
    the heuristic would say "has pressure" and then draw every point at the same width — which
    looks exactly like pressure being ignored.
  • Worth logging pointerType, pressure and getCoalescedEvents().length from the actual device
    before changing anything. Which of these it is decides the fix.

Leads for the breaking-up half

  • samplesOf (App.tsx:1138) only expands coalesced events when raw.length > 1, so it is not
    dropping the single-sample case.
  • More likely somewhere in the live overlay's rAF throttle, or a re-render dropping the in-progress
    stroke. Needs reproducing with the event stream logged.

Unknown whether the two are one bug or two; splitting this issue is fine once the device log says.

Activity

  1. difficcd commented on Oct 6, 2026

    @difficcd
    OwnerAuthor

    Both halves are now on main, found by measurement rather than on the device. Leaving this open until it is confirmed on the tablet.

    Pencil pressure ignored — #360. The width term was 0.65 + 0.35 * Math.min(1, pr * 2), and the * 2 puts the neutral 0.5 at 1×, so the clamp bit at exactly neutral: every press above it drew the same stroke to the pixel.

    pressure 0.1 0.25 0.5 0.6 0.75 0.9 1.0
    before 18 20 24 24 24 24 24
    after 18 20 24 26 28 32 34

    A stylus writes well above 0.5, so the whole range you actually press in was flat. Checked byte-for-byte that mouse strokes and every pressure at or below neutral render identically, so existing drawings are untouched.

    Strokes break up — #361. A PC has one pointer; a tablet has the pen and the palm. startDraw refused to begin from a touch, but nothing stopped a touch from feeding the gesture already running — the palm's moves were appended to the pen's stroke as points, and the palm lifting committed the stroke mid-line. The gesture already knew whose pointer it was; pointerId was set by begin() and read by nobody.

    To check on the tablet

    1. 필압 on, pencil, draw light → hard. The line should visibly thicken; before, anything past a light press was one fixed width.
    2. Draw a long line with your hand resting on the screen — that is the condition the break-up needs. It should stay one unbroken stroke.
    3. Existing drawings should look exactly as they did.

    If (2) still breaks up, it is a different cause from the palm and worth saying so — the pointer-ownership half is pinned by tests, so a remaining symptom points somewhere else.

  2. added
    needs-verificationMerged, but only a person on the device can confirm it - counted by scripts/process-metrics.mjs
    on Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-verificationMerged, but only a person on the device can confirm it - counted by scripts/process-metrics.mjs

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions