The @m3e/web/gestures module provides a gesture recognition subsystem supporting declarative and
programmatic gesture detection. It uses a modular recognizer architecture with a priority-based disposition
system that resolves competing claims on input.
Features include:
The @m3e/web/gestures module is organized around a base module that installs the gesture
subsystem and provides shared infrastructure for all recognizers.
import "@m3e/web/gestures";
Each recognizer is published as its own entry point, so applications can import only the gestures they use. For example:
import "@m3e/web/gestures/tap"; import "@m3e/web/gestures/swipe";
This section outlines usage examples and configuration guidance for the components in this package.
Gestures represent semantic actions (e.g., tap, pan, long-press) derived from analyzing sequences of pointer
events. Each gesture progresses through phases (start, update, end,
cancel) with recognizers emitting semantic detail at each phase.
start occurs when initial conditions are satisfied and recognition begins.update is emitted as the interaction continues and additional pointer events refine the
gesture.
end indicates that the gesture has completed successfully.cancel indicates that recognition failed or was interrupted.
Gestures can be recognized declaratively or programmatically. Declarative recognition uses
<m3e-*-gesture> elements bound to a target element using the for attribute.
When the recognizer determines that a gesture has entered a phase, it emits a gesture event
containing the phase and its associated detail. Programmatic recognition uses the
detectGesture function, which binds one or more recognizers to an element and invokes callback
handlers for each phase. These handlers receive the same gesture detail emitted by declarative usage, allowing
both approaches to produce identical results.
Gesture recognition can be configured with a set of common options that apply to all recognizers.
priority (default 1) determines the order in which recognizers compete to produce gestures.
buttons specifies which input buttons may be pressed. Valid values are
"primary" (default), "secondary", "middle", "back", and
"forward".
pointer-types (pointerTypes) limits recognition to particular pointer sources.
Declarative usage accepts a space-separated string, and programmatic usage accepts an array. Valid values
are "mouse", "pen", and "touch". By default, all pointer types are
allowed.
input-filter (inputFilter) provides an optional predicate that controls which
input is eligible for recognition.
All gestures emit a common set of detail fields that describe the semantic output of recognition.
gestureName identifies which gesture produced the detail.phase reports the current stage of the gesture lifecycle (start,
update, end, cancel).
inputId specifies the identifier of the input stream or streams that contributed to the
gesture.
timestamp records when the detail was generated.
Use the m3e-tap-gesture element or the tap function from the
@m3e/web/gestures/tap module to recognize a short press and release with minimal pointer
movement.
Tap the element below to see it in action.
<div id="item"></div> <m3e-tap-gesture for="item"></m3e-tap-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { tap } from "@m3e/web/gestures/tap";
const item = document.getElementById("item");
detectGesture(item, tap(phase({
onEnd: (detail) => {
// Handle tap
}
})));
In addition to common options, the tap recognizer provides settings that control how a tap is validated:
pointers specifies how many pointers must be pressed for the gesture to be recognized (default
1).
max-duration (maxDuration) defines the maximum allowed press time in milliseconds
(default 180).
max-displacement (maxDisplacement) sets the maximum permitted movement in pixels
(default 12).
max-press-interval (maxPressInterval) limits the time between the earliest and
latest pointerdown events (default 120).
max-release-interval (maxReleaseInterval) limits the time between the earliest and
latest pointerup events (default 120).
A tap gesture follows a simple lifecycle based on pointer events:
start phase when the required pointers produce a
pointerdown event and initial conditions are satisfied.
end phase when those pointers release with a
pointerup event.
cancel phase if any pointer is canceled.cancel phase if validation rules are exceeded, including
max-displacement (maxDisplacement),
max-duration (maxDuration),
max-press-interval (maxPressInterval), or
max-release-interval (maxReleaseInterval).
In addition to common detail, tap gesture events provide tap-specific fields that describe where
the interaction began and how long it lasted:
clientX and clientY report the viewport coordinates where the tap began.localX and localY report the element-relative coordinates where the tap began.
duration provides the total time of the interaction.
Use the m3e-long-press-gesture element or the longPress function from the
@m3e/web/gestures/long-press module to recognize a pointer held in place for a minimum duration
with minimal movement.
Press and hold the element below to see it in action.
<div id="item"></div> <m3e-long-press-gesture for="item"></m3e-long-press-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { longPress } from "@m3e/web/gestures/long-press";
const item = document.getElementById("item");
detectGesture(item, longPress(phase({
onEnd: (detail) => {
// Handle long-press
}
})));
The long-press recognizer shares the same options as tap, except for max-release-interval, and
includes the following additional settings:
discrete Whether the gesture is discrete. If true it starts after
minDuration while pointers stay down and ends on pointer-up. If false it starts
immediately when pointers are down and ends after minDuration. (default false)
min-duration (minDuration) specifies the minimum press time required for the
gesture to be recognized (default 500).
A long-press gesture progresses through start, end, and cancel phases.
The exact lifecycle depends on whether the gesture is continuous (default) or discrete.
start occurs immediately when pointers go down for continuous gestures, or after
minDuration for discrete gestures.
end occurs after minDuration for continuous gestures, or on
pointerup for discrete gestures.
cancel indicates that recognition failed or was interrupted. For discrete gestures,
cancellations before start are silent.
Long-press gesture events emit the same detail fields as tap, including the coordinates where the
press began and the total duration of the interaction.
Use the m3e-swipe-gesture element or the swipe function from the
@m3e/web/gestures/swipe module to recognize high-velocity directional swipe gestures.
Swipe the element below to see it in action.
<div id="item"></div> <m3e-swipe-gesture for="item"></m3e-swipe-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { swipe } from "@m3e/web/gestures/swipe";
const item = document.getElementById("item");
detectGesture(item, swipe(phase({
onEnd: (detail) => {
// Handle swipe
}
})));
Swipe gestures can be configured using the following options, which control how movement, velocity, and direction are interpreted during recognition:
pointers (pointers) specifies the number of pointers required for recognition
(default 1).
start-threshold (startThreshold) defines the minimum distance (px) a pointer must
move before the gesture begins (default 4).
min-velocity (minVelocity) specifies the minimum velocity (px/ms) required to
recognize a swipe (default 0.3).
directions lists the allowed swipe directions (default
["left","right","up","down"]).
direction-threshold (directionThreshold) defines the minimum displacement (px)
required before a direction is considered valid (default 12).
direction-grace-period (directionGracePeriod) sets the maximum time (ms) a pointer
may move in an uncommitted or disallowed direction before rejection (default 0).
min-displacement (minDisplacement) specifies the minimum distance (px) required
before the gesture can be recognized (default 24).
max-press-interval (maxPressInterval) defines the maximum allowed time (ms)
between the earliest and latest press (default 120).
A swipe gesture progresses through all phases and is recognized on pointerup once its velocity,
displacement, and direction requirements have been met.
start occurs when the required pointers produce a pointerdown event and initial
conditions are satisfied.
update is emitted during movement as pointermove events refine the gesture. The
recognizer tracks displacement, velocity, and direction during this phase.
end occurs on pointerup when the gesture meets the required velocity,
displacement, and direction thresholds. Recognition happens at release rather than during movement.
cancel indicates that recognition failed or was interrupted, including exceeding validation
rules such as max-displacement, max-duration, max-press-interval, or
violating direction constraints.
Swipe gestures emit detail describing the resolved direction, movement, velocity, and initial input coordinates:
direction ("left" | "right" | "up" | "down") specifies the resolved swipe
direction.
axis ("x" | "y") indicates the dominant axis of movement.startClientX and startClientY provide the viewport coordinates of the initial
input sample.
startLocalX and startLocalY provide the element-relative coordinates of the
initial input sample.
translationX and translationY report the total movement in pixels along each axis.
velocityX and velocityY report instantaneous velocity in pixels per millisecond.
speed provides the velocity magnitude in pixels per millisecond.displacement reports the total movement in pixels.duration specifies the total time in milliseconds from the initial input sample to gesture
completion.
Use the m3e-pan-gesture element or the pan function from the
@m3e/web/gestures/pan module to track continuous pointer movement across an element.
Pan the element below to see it in action.
<div id="item"></div> <m3e-pan-gesture for="item"></m3e-pan-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { pan } from "@m3e/web/gestures/pan";
const item = document.getElementById("item");
detectGesture(item, pan(phase({
onStart: (detail) => {
// Begin tracking
},
onUpdate: (detail) => {
// Continuous movement
},
onEnd: (detail) => {
// Finalize pan
},
onCancel: (detail) => {
// Abort pan
}
})));
Pan gestures support options that control activation, movement thresholds, axis behavior, and multi-pointer coordination:
pointers specifies the number of pointers required for the gesture to be recognized. A value of
1 enables single-pointer panning, while higher values allow multi-pointer movement tracking
(default 1).
activation-mode (activationMode) determines how the gesture activates. When set to
"press", the gesture activates as soon as the pointer is pressed. When set to
"move", activation occurs only after movement begins (default "press").
min-displacement (minDisplacement) specifies the minimum movement in pixels
required before the gesture begins emitting updates. This prevents accidental activation from minor jitter
(default 4).
lock-axis (lockAxis) determines whether movement is locked to a specific axis.
When set to "auto", the dominant axis is chosen automatically once sufficient displacement is
detected (default "none").
axis-threshold (axisThreshold) defines the minimum total displacement in pixels
required before axis locking resolves when lockAxis is "auto" (default 8).
delta-threshold (deltaThreshold) specifies the minimum incremental movement in
pixels on the secondary axis required before emitting detail when the gesture is locked to a primary axis
(default 0).
max-press-interval (maxPressInterval) defines the maximum allowed time in
milliseconds between the earliest and latest press when multiple pointers are required. This ensures
multi-pointer activation occurs within a consistent time window (default 120).
A pan gesture progresses through all phases and is recognized continuously as the pointer moves. It begins
once the required pointers are pressed and the minimum displacement has been met, emits updates throughout
movement, and completes on pointerup with the final translation and axis information.
start occurs when the required pointers produce a pointerdown event and initial
conditions are satisfied.
update is emitted during movement as pointermove events refine the gesture. The
recognizer tracks displacement, velocity, and axis behavior during this phase.
end occurs on pointerup and provides the final translation values and resolved
axis. Recognition happens at release rather than during movement.
cancel indicates that recognition failed or was interrupted, including exceeding validation
rules such as max-press-interval or losing required pointers.
The pan gesture detail describes the semantic output of continuous pointer movement. It reports both incremental and accumulated motion, along with positional, directional, and temporal information derived from the input stream.
startClientX and startClientY are the viewport coordinates of the initial input
sample.
startLocalX and startLocalY are the element-relative coordinates of the initial
input sample.
clientX and clientY are the viewport coordinates of the most recent input sample.
localX and localY are the element-relative coordinates of the most recent input
sample.
deltaX and deltaY represent incremental movement in pixels between the last two
samples.
displacement is the Euclidean magnitude of the incremental movement.totalDeltaX and totalDeltaY represent total movement in pixels from the initial
sample.
totalDisplacement is the Euclidean magnitude of the total movement.axis indicates the dominant axis of movement, either x or y.velocityX and velocityY are the instantaneous velocities in pixels per
millisecond.
directionX and directionY represent movement direction along each axis as
-1, 0, or 1.
speed is the magnitude of the velocity vector.angle is the movement angle in radians, computed from total displacement.duration is the total time in milliseconds from the initial sample.deltaTime is the time in milliseconds between the last two samples.
Use the m3e-rotate-gesture element or the rotate function from the
@m3e/web/gestures/rotate module to detect rotational movement from multiple pointers.
Rotate the element below using two pointer devices (two fingers) to see the gesture in action.
<div id="item"></div> <m3e-rotate-gesture for="item"></m3e-rotate-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { rotate } from "@m3e/web/gestures/rotate";
const item = document.getElementById("item");
detectGesture(item, rotate(phase({
onStart: (detail) => {
// Begin tracking
},
onUpdate: (detail) => {
// Continuous movement
},
onEnd: (detail) => {
// Finalize rotate
},
onCancel: (detail) => {
// Abort rotate
}
})));
Rotate gesture options control how multi-pointer rotation is interpreted. They define the number of pointers required, the activation mode, the minimum displacement needed to begin rotation, and the maximum allowed interval between pointer presses.
pointers specifies the number of pointers required for rotation to be recognized (default 2).
activation-mode (activationMode) defines how the gesture activates, using the same
modes available to pan (default press).
min-displacement (minDisplacement) is the minimum centroid displacement in pixels
required before rotation begins (default 4).
max-press-interval (maxPressInterval) specifies the maximum allowed time in
milliseconds between the earliest and latest pointer press (default 120).
A rotate gesture progresses through all phases and is recognized once the required number of pointers have pressed within the allowed interval and rotation has begun. The recognizer computes pointer angles relative to the centroid and emits detail as rotation changes over time.
start occurs when rotation first becomes detectable, after the centroid has displaced beyond
min-displacement and the initial angle is established.
update is emitted as the pointers rotate around the centroid. The recognizer reports
incremental rotation, velocity, and the current average angle during this phase.
end occurs when all pointers are released or otherwise resolve successfully. Recognition
happens immediately once rotation concludes.
cancel indicates that recognition failed or was interrupted. Cancellation occurs when the press
interval exceeds max-press-interval, when required pointers are lost, or when rotation cannot
continue.
The rotate gesture detail describes the semantic output of multi-pointer rotation. It reports the initial angle, current angle, total rotation, incremental rotation, and angular velocity derived from pointer movement around the centroid.
initialAngle is the angle in radians of all pointers relative to the centroid at activation.
currentAngle is the average angle in radians of all pointers relative to the centroid at the
most recent sample.
rotation is the total rotation in radians since activation.rotationDelta is the incremental rotation in radians between the last two samples.rotationVelocity is the instantaneous angular velocity in radians per millisecond.
Use the m3e-scale-gesture element or the scale function from the
@m3e/web/gestures/scale module to detect multi-pointer scaling. A scale gesture measures how far
active pointers move toward or away from their centroid, producing a scale factor that represents zoom in or
out.
Pinch the element below using two pointer devices (two fingers) to see the gesture in action.
<div id="item"></div> <m3e-scale-gesture for="item"></m3e-scale-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { scale } from "@m3e/web/gestures/scale";
const item = document.getElementById("item");
detectGesture(item, scale(phase({
onStart: (detail) => {
// Begin tracking
},
onUpdate: (detail) => {
// Continuous movement
},
onEnd: (detail) => {
// Finalize rotate
},
onCancel: (detail) => {
// Abort rotate
}
})));
Scale gesture options control how multi-pointer zooming is interpreted. They define the number of pointers required, the minimum displacement needed to begin scaling, and the maximum allowed interval between pointer presses.
pointers specifies the number of pointers required for scaling to be recognized (default 2).
min-displacement (minDisplacement) is the minimum average distance change in
pixels required before scaling begins (default 4).
max-press-interval (maxPressInterval) specifies the maximum allowed time in
milliseconds between the earliest and latest pointer press (default 120).
A scale gesture progresses through all phases and is recognized once the required number of pointers have pressed within the allowed interval and the centroid has displaced enough to begin scaling. The recognizer computes distances from each pointer to the centroid and emits detail as the scale factor changes over time.
start occurs when scaling first becomes detectable, after the average pointer distance from the
centroid has changed beyond min-displacement and the initial distance is established.
update is emitted as the pointers move closer or farther from the centroid. The recognizer
reports incremental scale changes, velocity, and the current average distance during this phase.
end occurs when all pointers are released or otherwise resolve successfully. Recognition
happens immediately once scaling concludes.
cancel indicates that recognition failed or was interrupted. Cancellation occurs when the press
interval exceeds max-press-interval, when required pointers are lost, or when scaling cannot
continue.
The scale gesture detail describes the semantic output of multi-pointer scaling. It reports the initial and current average distances from the centroid, the total scale factor, incremental scale changes, and instantaneous scale velocity.
initialDistance is the average distance in pixels from each active pointer to the centroid at
activation.
currentDistance is the average distance in pixels from each active pointer to the centroid at
the most recent sample.
scale is the total scale factor representing zoom since activation.scaleDelta is the incremental scale change between the last two samples.scaleVelocity is the instantaneous scale velocity in units per millisecond.
Use the m3e-repeat-gesture element or the repeat function from the
@m3e/web/gestures/repeat module to detect repeated activations of a gesture over time. The
element must contain a single nested gesture element that defines which gesture is repeated, and the
repeat function requires the recognizer to repeat to be explicitly provided.
Tipple tap the element below to see repeat behavior in action.
<div id="item" class="target"></div> <m3e-repeat-gesture for="item" count="3" max-interval="350"> <m3e-tap-gesture></m3e-tap-gesture> </m3e-repeat-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { tap } from "@m3e/web/gestures/tap";
import { repeat } from "@m3e/web/gestures/repeat";
const item = document.getElementById("item");
detectGesture(item, repeat(
phase({
onEnd: (detail) => {
// Handle triple tap
},
}),
{
count: 3,
maxInterval: 350
},
tap(),
));
Repeat gesture options control how consecutive activations of a nested gesture are interpreted. They define the maximum allowed interval between occurrences and the number of repetitions required before the repeat gesture is recognized.
max-interval (maxInterval) specifies the maximum allowed time in milliseconds
between consecutive gesture occurrences. If the interval between activations exceeds this value, the
repeated gesture fails (default 250).
count specifies the number of times the gesture must be repeated before recognition succeeds. A
value of 2 detects a double activation, while higher values require additional repetitions
(default 2).
A repeat gesture progresses through all phases and is recognized once its nested gesture has produced the required number of terminal occurrences within the allowed interval. Each occurrence contributes a terminal detail, and the repeat recognizer emits its own detail stream as repetitions accumulate.
start occurs when the nested gesture produces its first terminal detail. The repeat recognizer
begins tracking repetitions and starts the interval timer.
update is emitted for each subsequent terminal occurrence as additional details are collected.
After emitting update, the nested recognizer is reset to allow the next occurrence to begin,
and the interval timer is refreshed.
end occurs when the nested gesture has produced the required number of terminal occurrences and
all accepted pointers have resolved. Recognition happens immediately once both count and acceptance
conditions are satisfied.
cancel indicates that recognition failed or was interrupted. Cancellation occurs when the
interval between occurrences exceeds max-interval, when the nested recognizer produces a
terminal cancel detail after repetitions have begun, or when accepted pointers are rejected.
Any condition that prevents the nested gesture from completing its next occurrence results in cancellation.
The repeat gesture detail describes the semantic output of a repeated gesture. It aggregates the terminal details produced by each occurrence of the nested gesture in the order they were recognized.
details is the ordered list of terminal gesture details that form the repeated gesture. Each
entry represents one completed occurrence of the nested gesture.
Use the m3e-sequence-gesture element or the sequence function from the
@m3e/web/gestures/sequence module to detect ordered activations of multiple gestures over time.
The element must contain the gesture elements in the order they must occur, and the
sequence function requires the recognizers to be explicitly provided.
Press and hold the element below, then pan to see a sequential gesture in action.
<div id="item" class="target"></div> <m3e-sequence-gesture for="item" max-interval="0"> <m3e-long-press-gesture></m3e-long-press-gesture> <m3e-pan-gesture activation-mode="move"></m3e-pan-gesture> </m3e-sequence-gesture>
import { detectGesture, phase } from "@m3e/web/gestures";
import { longPress } from "@m3e/web/gestures/long-press";
import { pan } from "@m3e/web/gestures/pan";
import { sequence } from "@m3e/web/gestures/sequence";
const item = document.getElementById("item");
detectGesture(
item,
sequence(
{ maxInterval: 0 },
longPress(
phase({
onEnd: (detail) => {
// Handle pick up
},
}),
),
pan(
{
activationMode: "move",
},
phase({
onUpdate: (detail) => {
// Handle move
},
}),
),
),
);
Sequence gesture options control how ordered gesture activations are interpreted. They define the maximum allowed interval between each gesture in the sequence, ensuring that the sequence progresses within a consistent time window.
max-interval (maxInterval) specifies the maximum allowed time in milliseconds
between each gesture in the sequence. If the interval between activations exceeds this value, the sequence
fails (default 250).
A sequence gesture progresses through all phases and is recognized once each gesture in the sequence has been activated in order within the allowed interval. The recognizer advances through its nested gestures one by one, emitting detail as each step completes.
start occurs when the first gesture in the sequence produces its terminal detail. The sequence
begins tracking progress and starts the interval timer.
update is emitted each time the next gesture in the sequence completes. After emitting
update, the recognizer advances to the next gesture and refreshes the interval timer. Progress
only continues when gestures occur in the defined order.
end occurs when all gestures in the sequence have been activated in order within the configured
interval. Recognition happens immediately once the final gesture completes.
cancel indicates that recognition failed or was interrupted. Cancellation occurs when the
interval between steps exceeds max-interval, when a gesture completes out of order, when a
nested recognizer produces a terminal cancel detail after the sequence has begun, or when any
condition prevents the next gesture in the sequence from completing.
The sequence gesture detail describes the semantic output of an ordered series of gestures. It aggregates the terminal details produced by each gesture in the sequence in the order they were recognized.
details is the ordered list of terminal gesture details that form the sequence. Each entry
represents one completed step in the sequence.
By default, gesture elements are not given an ARIA role, indicating that they are treated as neutral, non-semantic elements in the accessibility tree unless explicitly referenced or annotated.
The @m3e/web package uses
JavaScript Modules. To use it directly in a browser without a bundler, use a module script similar to the following.
<script type="module" src="/node_modules/@m3e/web/dist/gestures.js"></script>
To include specific recognizers:
<script type="module" src="/node_modules/@m3e/web/dist/gestures-long-press.js"></script> <script type="module" src="/node_modules/@m3e/web/dist/gestures-pan.js"></script> <script type="module" src="/node_modules/@m3e/web/dist/gestures-repeat.js"></script> <script type="module" src="/node_modules/@m3e/web/dist/gestures-rotate.js"></script> <script type="module" src="/node_modules/@m3e/web/dist/gestures-scale.js"></script> <script type="module" src="/node_modules/@m3e/web/dist/gestures-sequence.js"></script> <script type="module" src="/node_modules/@m3e/web/dist/gestures-swipe.js"></script> <script type="module" src="/node_modules/@m3e/web/dist/gestures-tap.js"></script> <script type="module" src="/node_modules/@m3e/web/dist/gestures-transform.js"></script>
In addition, you must use an import map to include dependencies.
<script type="importmap">
{
"imports": {
"tslib": "https://cdn.jsdelivr.net/npm/tslib@2.8.1/+esm",
"lit": "https://cdn.jsdelivr.net/npm/lit@3.3.0/+esm",
"lit/": "https://cdn.jsdelivr.net/npm/lit@3.3.0/",
"lit-html": "https://cdn.jsdelivr.net/npm/lit-html@3.3.0/+esm",
"lit-html/directive.js": "https://cdn.jsdelivr.net/npm/lit-html@3.3.0/directive.js",
"@lit/reactive-element": "https://cdn.jsdelivr.net/npm/@lit/reactive-element@2.0.4/+esm",
"@lit/reactive-element/": "https://cdn.jsdelivr.net/npm/@lit/reactive-element@2.0.4/",
"@m3e/web/core": "/node_modules/@m3e/web/dist/core.js"
"@m3e/web/gestures": "/node_modules/@m3e/web/dist/gestures.js"
}
}
</script>
For production builds, use the minified files to ensure optimal load performance.
The @m3e/web package includes a
Custom Elements Manifest
(custom-elements.json), which documents the properties, attributes, slots, events and CSS custom
properties of each component.
You can explore the API below, or integrate the manifest into your own tooling.