How to Start a Touchscreen Gesture in Puppeteer
Start a touch with Puppeteer’s touchscreen API, continue it with move(), and finish it with end(). Learn when to use tap(), how to handle edge cases, and how to troubleshoot gestures.
To start a touchscreen gesture in Puppeteer, call await page.touchscreen.touchStart(x, y). It returns a TouchHandle; use that handle’s move(nextX, nextY) method to continue the same touch, then call end() to release it. For a simple coordinate tap, use page.touchscreen.tap(x, y) instead.
const touch = await page.touchscreen.touchStart(120, 240);
await touch.move(160, 260);
await touch.end();
This sequence models a held touch followed by movement and release. The coordinate values should match the page and viewport you are automating. See the official touchStart reference and TouchHandle reference.
1. Set up a page that accepts touch input
Use a current Puppeteer installation and navigate to the page under test before sending touch input. If you are testing a mobile layout, configure the viewport and touch emulation before navigation so the page loads in the intended context.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({
width: 390,
height: 844,
isMobile: true,
hasTouch: true,
});
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
// Start, move, and end a touch here.
} finally {
await browser.close();
}
The official interaction guide describes Puppeteer’s page interaction APIs, including touch and element interactions: Page interactions. Choose the viewport and page readiness condition that fit your test; this example does not imply a universal device-compatibility guarantee.
2. Start, move, and end a gesture
touchStart(x, y) dispatches a touchstart at the given coordinates and resolves to a handle. Keep that handle for the duration of the gesture. Call move(x, y) on it for each desired point, then call end() to dispatch the release.
const touch = await page.touchscreen.touchStart(120, 240);
try {
await touch.move(140, 245);
await touch.move(165, 255);
await touch.move(190, 270);
} finally {
await touch.end();
}
Use try/finally when later steps might throw, so your automation still attempts to release the touch. Keep the handle associated with the touch that started it; do not substitute a new coordinate tap when you need a continuous gesture.
Choose coordinates in the page viewport
Touch coordinates are viewport positions. Make sure the target is visible at those coordinates at the time the gesture begins. Scrolling, responsive layout changes, overlays, and asynchronous rendering can all move the intended target. If the target is an element, consider element-based tapping or a locator for ordinary interactions.
3. Choose between a gesture, a tap, and an element tap
| Need | Use | Behavior |
|---|---|---|
| Hold contact, move, then release | touchStart(), handle move(), end() |
Explicit touch sequence with coordinate control. |
| Tap a coordinate | page.touchscreen.tap(x, y) |
Dispatches touchstart followed by touchend. |
| Tap an element | elementHandle.tap() |
Scrolls the element into view if needed and taps its center. |
| Interact with a page element using readiness checks | Puppeteer Locator API | Locators are recommended in the guide for ordinary element interactions. |
Use the explicit handle when the touch must remain active or follow a path. A tap is simpler when the action is a single press and release. Element tap is useful when an element’s center is an appropriate target; for more control over the touch path, use coordinates. References: touchscreen.tap(), ElementHandle.tap(), and the interaction guide.
4. Complete runnable example
This example opens a page, enables a touch-capable mobile viewport, starts a touch at explicit coordinates, moves it, releases it, and closes the browser even if an operation fails.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({
width: 390,
height: 844,
isMobile: true,
hasTouch: true,
});
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
const startX = 120;
const startY = 240;
const touch = await page.touchscreen.touchStart(startX, startY);
try {
await touch.move(startX + 30, startY + 10);
await touch.move(startX + 70, startY + 25);
} finally {
await touch.end();
}
} finally {
await browser.close();
}
Replace the example URL and coordinates with the page and interaction your test requires. The API documentation does not define a universal gesture duration, delay between moves, or guarantee for every page and device configuration.
5. Other ways to call the API
The core subject is Puppeteer’s JavaScript API. These alternatives can be useful when the surrounding automation is driven from a shell, Python, or Node.js script, but they do not change how touchStart() works.
cURL: take a screenshot after a gesture with ScreenshotNeo
cURL cannot call Puppeteer’s in-process page API. It can request a screenshot after your browser automation has performed the gesture and the target page is in the desired state.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
Python: drive Puppeteer from a Node subprocess
Puppeteer is a Node.js library. A Python program can invoke a Node script that performs the gesture; the following Python wrapper assumes that the runnable JavaScript has been saved as gesture.mjs.
import subprocess
subprocess.run(["node", "gesture.mjs"], check=True)
Node.js: call Puppeteer directly
The complete runnable JavaScript example above is the direct Node.js method. If you instead need only a screenshot of a URL, the ScreenshotNeo request is:
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);
6. Or skip the browser setup
If you need a screenshot of a URL rather than a custom touch sequence, ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF. The call below follows the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers say the page verdict and whether the request was billed. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots.
Sign up for 1,000 free screenshots a month, with no card required.
7. Troubleshooting touch gestures
| Symptom | Likely cause | What to check |
|---|---|---|
| No visible interaction | The start coordinates miss the target, the element is covered, or the page is not ready. | Confirm the current viewport, inspect the target position, and wait for the page state your test needs before starting. |
| The gesture acts like a tap | The touch is ended immediately or movement is not being applied through its handle. | Keep the handle returned by touchStart(), call move() on it, and call end() after the movement sequence. |
| Some intermediate movement is not observed | Browser optimizations can throttle touch moves; not every call necessarily appears as a touchmove event. |
Test the page’s resulting behavior rather than requiring one event per method call. See the official touchMove reference. |
| Element tap misses | The element center may not be the intended interaction point, or page layout changed. | Use explicit coordinates when the center is unsuitable; re-check layout and visibility after scrolling or rendering. |
| A later step remains in a pressed state | An error interrupted the sequence before release. | Put touch.end() in a finally block so release is attempted even when movement fails. |
8. Reliability and performance notes
- Do not assume one event per move call. Chrome may throttle touch movement, so assert the page’s meaningful outcome where possible rather than an exact count of intermediate events.
- Keep the path purposeful. Send only the coordinates needed to describe the interaction; more calls mean more automation steps, without a documented guarantee that all become separate browser events.
- Stabilize the page before starting. A late layout shift or overlay can invalidate otherwise correct coordinates. Use an appropriate navigation condition or wait for the relevant page state.
- Always release. Ending a touch in cleanup makes failures easier to diagnose and prevents later actions from inheriting an unfinished sequence.
- Cost depends on your browser runtime. Puppeteer itself does not specify a price in the cited API references; runtime and infrastructure costs depend on where you run the browser. The separate ScreenshotNeo service offers a free tier and listed paid plans on its product terms.
9. FAQ
Does touchStart() finish the gesture?
No. It starts the touch and returns a handle. Move that touch with the handle and call end() to release it.
Can I use tap() for a swipe?
No. tap() models touchstart followed by touchend. Use a touch handle when the interaction needs movement while contact remains active.
Will every call to move() emit a touchmove event?
Not necessarily. Browser optimizations can throttle moves; Puppeteer’s documentation explicitly cautions that not every call produces a browser event.
Should I use coordinates for every interaction?
No. Use element tap or Locators when the target is an element and you want Puppeteer’s element targeting and readiness behavior. Use coordinates when the exact touch path matters.


