Why touch events matter
Modern smartphones and tablets rely on capacitive screens that capture finger input, and as the mobile web matures, applications increasingly need native support for those interactions. Fast-paced games, for instance, typically require players to press several buttons simultaneously — a scenario that demands proper multi-touch handling rather than simple single-pointer events.
Apple introduced its touch events API in iOS 2.0, and Android has since converged on that de-facto standard. A W3C working group is now formalizing the specification. The API is implemented on iOS and Android devices, as well as desktop Chrome on hardware with touch support.
The core API
Three basic events are widely implemented:
- touchstart: a finger is placed on a DOM element.
- touchmove: a finger is dragged along a DOM element.
- touchend: a finger is removed from a DOM element.
Each event carries three lists of touches:
- touches: all fingers currently on the screen.
- targetTouches: fingers on the current DOM element.
- changedTouches: fingers involved in the current event. For example, in a
touchendevent, this is the finger that was removed.
Each item in these lists exposes touch-specific data:
- identifier: a unique number for the finger in the touch session.
- target: the DOM element receiving the action.
- client/page/screen coordinates: the location of the action.
- radius coordinates and rotationAngle: an ellipse approximating the finger shape.
These events cover virtually any touch-based interaction, including familiar multi-touch gestures like pinch-zoom and rotation. A simple snippet for dragging a DOM element with a single finger looks like:
var obj = document.getElementById('id');
obj.addEventListener('touchmove', function(event) {
// If there's exactly one finger inside this element
if (event.targetTouches.length == 1) {
var touch = event.targetTouches[0];
// Place element where the finger is
obj.style.left = touch.pageX + 'px';
obj.style.top = touch.pageY + 'px';
}
}, false);
A useful sample displays all current touches on the screen to give a feel for device responsiveness:
// Setup canvas and expose context via ctx variable
canvas.addEventListener('touchmove', function(event) {
for (var i = 0; i < event.touches.length; i++) {
var touch = event.touches[i];
ctx.beginPath();
ctx.arc(touch.pageX, touch.pageY, 20, 0, 2*Math.PI, true);
ctx.fill();
ctx.stroke();
}
}, false);
Several multi-touch demos showcase the API's range, including a canvas-based drawing demo and Browser Ninja, a Fruit Ninja clone built with CSS3 transforms and canvas:
Making multi-touch behave
Disable default browser behavior
Default viewport settings conflict with multi-touch gestures because swipes are often interpreted as scrolling or zooming. Disable user scaling with a viewport meta tag:
<meta name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no>
Some mobile devices also add default behavior for touchmove, such as the classic iOS overscroll bounce. This can confuse multi-touch apps, but is easily suppressed:
document.body.addEventListener('touchmove', function(event) {
event.preventDefault();
}, false);
Handle many touches efficiently
Complex multi-finger apps receive a flood of events, so careful rendering matters. Drawing immediately on each touch input does not scale with the number of fingers on screen:
canvas.addEventListener('touchmove', function(event) {
renderTouches(event.touches);
}, false);
A better approach tracks all fingers and renders in a continuous loop:
var touches = []
canvas.addEventListener('touchmove', function(event) {
touches = event.touches;
}, false);
// Setup a 60fps timer
timer = setInterval(function() {
renderTouches(touches);
}, 15);
Target the right touch list
event.touches contains all fingers on the screen, not just those on the element's target. event.targetTouches and event.changedTouches are usually more precise for element-specific logic.
General mobile best practices apply here too; see Eric Bidelman's overview and the W3C mobile web best practices.
Browser support reality check
Touch event support varies significantly between implementations. A diagnostics script was used to test Android 2.3.3 on Nexus One and Nexus S, Android 3.0.1 on Xoom, and iOS 4.2 on iPad and iPhone.
All tested browsers support touchstart, touchend, and touchmove, as well as the touches, targetTouches, and changedTouches lists. But none support these additional spec events:
- touchenter: a moving finger enters a DOM element.
- touchleave: a moving finger leaves a DOM element.
- touchcancel: a touch is interrupted.
None of the tested browsers expose radiusX, radiusY, or rotationAngle. During touchmove, events fire roughly 60 times per second across all devices.
Platform quirks
Android 2.3.3 (Nexus One, Nexus S): no multi-touch support — a known issue.
Android 3.0.1 (Xoom): basic multi-touch works, but only on a single DOM element. The browser fails to respond to simultaneous touches on different elements. This reacts properly:
obj1.addEventListener('touchmove', function(event) {
for (var i = 0; i < event.targetTouches; i++) {
var touch = event.targetTouches[i];
console.log('touched ' + touch.identifier);
}
}, false);
But this does not:
var objs = [obj1, obj2];
for (var i = 0; i < objs.length; i++) {
var obj = objs[i];
obj.addEventListener('touchmove', function(event) {
if (event.targetTouches.length == 1) {
console.log('touched ' + event.targetTouches[0].identifier);
}
}, false);
}
iOS 4.x (iPad, iPhone): fully supports multi-touch, tracks many fingers, and provides a responsive touch experience.
Touch support on the desktop
Mobile development often begins with desktop prototypes, but multi-touch is difficult to test without touch hardware. Deploying each change to a server and then reloading on a device slows iteration, and debugging on phones or tablets is constrained by limited browser tooling.
Simulating touch events on a development machine shortens the loop. For single-touch, Chrome's developer tools offer touch event emulation under the Settings gear → "Overrides" or "Emulation" → "Emulate touch events". Other options include Phantom Limb, which simulates touch events and provides a visual hand, and the Touchable jQuery plugin that unifies touch and mouse events.
For true multi-touch testing on a laptop with a touch-enabled trackpad, the MagicTouch.js polyfill captures trackpad input and translates it into standard-compatible touch events:
- Install the npTuioClient NPAPI plugin into
~/Library/Internet Plug-Ins/. - Download and run the TongSeng TUIO app for Mac's MagicPad.
- Include
MagicTouch.js, which converts npTuioClient callbacks into spec-compatible touch events. - Reference both the script and plugin in the app:
<head>
...
<script src="https://web.dev/path/to/magictouch.js"></script>
</head>
<body>
...
<object id="tuio" type="application/x-tuio" style="width: 0px; height: 0px;">
Touch input plugin failed to load!
</object>
</body>
The plugin may need to be enabled manually. A live demonstration is at paulirish.com/demo/multi. This approach was verified with Chrome 10, but should work in other modern browsers with only minor adjustments.
For machines without multi-touch input, other TUIO trackers like reacTIVision can simulate events; see the TUIO project page. On OS X, note that gestures may conflict with system-level multi-touch actions — these can be configured under Trackpad in System Preferences.



