How to Move a Touchscreen Gesture in Puppeteer
Move an active Puppeteer touch with its TouchHandle, or use the page-level touchscreen API. Learn the lifecycle, coordinate rules, event caveats, and fixes for common errors.
To move a touch that has already started in Puppeteer, keep the TouchHandle returned by page.touchscreen.touchStart(x, y), call touch.move(newX, newY), and finish with touch.end(). For a single active touch, you can instead call page.touchscreen.touchMove(x, y) and page.touchscreen.touchEnd().
const touch = await page.touchscreen.touchStart(100, 200);
await touch.move(180, 240);
await touch.end();
The coordinates are horizontal x and vertical y positions. Puppeteer does not guarantee that every move call becomes a DOM touchmove event: browsers can throttle or suppress individual events.
1. Start, move, and end a touch with TouchHandle
touchStart() dispatches the start of a touch and returns a handle for that active touch. The handle keeps the gesture lifecycle together, which makes it the clearest approach when you want to refer to the specific touch you started.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewportSize({ width: 390, height: 844 });
await page.goto('https://example.com');
const touch = await page.touchscreen.touchStart(100, 200);
await touch.move(180, 240);
await touch.move(260, 300);
await touch.end();
} finally {
await browser.close();
}
Replace the example URL and coordinates with the page and positions your interaction needs. Each move() takes the new position, not a distance to add to the previous position.
Use try/finally when a gesture may fail midway
If code between the start and end can throw, make a best effort to end the active touch before propagating the error. Do not call end() twice or continue moving a handle after it has ended.
const touch = await page.touchscreen.touchStart(100, 200);
try {
await touch.move(180, 240);
// Perform any checks that depend on the gesture here.
} finally {
await touch.end();
}
2. Use the page-level Touchscreen methods
For straightforward interactions, the page-level methods express the same start-move-end sequence without storing a handle:
await page.touchscreen.touchStart(100, 200);
await page.touchscreen.touchMove(180, 240);
await page.touchscreen.touchMove(260, 300);
await page.touchscreen.touchEnd();
The documented touchMove() and touchEnd() methods operate on the first active touch. Use the handle form when explicit ownership of the started touch makes the code easier to reason about. A page’s touchscreen is available as page.touchscreen.
3. Understand coordinates and browser event behavior
- Coordinates:
xis the horizontal position andyis the vertical position. For example,touchStart(100, 200)starts at horizontal 100, vertical 200;move(180, 240)changes the position to horizontal 180, vertical 240. - Moves are not guaranteed DOM events: Chromium and other browsers may optimize, throttle, or suppress individual touch move events. An awaited Puppeteer move call does not promise one
touchmovehandler invocation or one animation frame. - Keep the lifecycle valid: Move and end an active touch. The API documents a
TouchErrorif code tries to move or end a touch that does not exist. - Check your installed version: The official documentation pages surfaced for this API carry different version labels. Confirm the method signatures against the Puppeteer version installed in your project.
4. Troubleshoot common problems
| Symptom | Likely cause | What to do |
|---|---|---|
TouchError while moving |
The touch is no longer active, was never started successfully, or the handle was already ended. | Start a touch and retain its returned handle. Ensure cleanup does not end it before later moves. |
TouchError while ending |
There is no active touch to end, or the same touch was already ended. | End each active touch once. Keep start, move, and end in one control flow where possible. |
| The page does not react to every move | The browser can throttle or suppress touchmove events; application behavior may also depend on its event handlers. | Do not assume every call is a separate DOM event or animation frame. Check the interaction’s final state and the page’s touch handling. |
| The gesture goes in an unexpected direction | Coordinates were treated as offsets, or horizontal and vertical positions were swapped. | Pass absolute positions as (x, y), with x horizontal and y vertical. |
| A touchscreen method is missing or its signature differs | The installed Puppeteer version may differ from the documentation version you consulted. | Check the local package version and its API reference, then use the methods exposed by that installed version. |
5. Reliability and performance notes
Keep each gesture’s start, moves, and end in a predictable sequence, and make sure cleanup runs when an intermediate step fails. A handle makes it easier to associate the end operation with the touch that began the interaction. The page-level form is concise when the first-active-touch behavior is appropriate.
Do not add move calls solely to force a particular number of browser events; browser optimizations mean calls do not map one-to-one to events. Use only the points needed to describe the interaction, and validate the page outcome that matters to your automation. Puppeteer gesture calls have no per-shot cost; browser and compute costs depend on how you run your own automation and are not specified by the API references cited here.
6. Or skip the browser setup
If your task is to capture a page image rather than automate its touch interaction, ScreenshotNeo provides a website screenshot API and MCP server. A single request returns an image or PDF, without setting up Puppeteer for a screenshot.
For example, this cURL request saves a WebP screenshot of a page:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for options and other request examples. Cookie banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up free for ScreenshotNeo.
7. FAQ
Can I move a touch without ending it immediately?
Yes. Keep the returned handle and call move() as needed; call end() when the gesture is complete.
Does Puppeteer support moving multiple active touches with these methods?
The page-level touchMove() and touchEnd() methods documented here target the first active touch. The handle-based approach lets you keep a reference to the touch returned by its start call.
Does one move call always fire one touchmove event?
No. Browser optimizations can throttle or suppress individual events.


