Visualization
Visualization helps you answer one question quickly: what did the browser actually look like when the script ran?
What to use
Section titled “What to use”- Mouse pointer visualizer plugin for an on-page cursor overlay.
- Keyboard visualizer plugin for key taps, combos, and active keys overlay.
- Screenshots for fast visual snapshots.
- Full workflow guide when you want these pieces combined with waits and evaluation.
Plugin decoupling
Section titled “Plugin decoupling”The mouse pointer visualizer coordinates with optional plugins (such as interaction blocking) through host-side plugin messages, not browser DOM events. This keeps plugin code decoupled so you can remove optional plugins without changing visualization code.
Visualization flow
Section titled “Visualization flow”flowchart LR A[Start view] --> B[Attach pointer overlay] B --> C[Run actions] C --> D[Capture screenshot] D --> E[Inspect output]Pointer overlay
Section titled “Pointer overlay”Use the pointer visualizer when you want click paths or hover positions to be obvious in the page.
import { startMousePointerVisualizer } from "webotron/plugins/mouse-pointer-visualizer";
const pointer = await startMousePointerVisualizer(view, { color: "#ff3b30", size: 24, withLabel: true, trackPageMouseMoves: true,});
await pointer.moveTo(320, 180);await pointer.visualizeClick("left", 1); // overlay pulse only, no real clickawait pointer.click();await pointer.moveBy(120, 40);Use visualizeClick(...) when you want to show intent in demos/debug runs without dispatching a real click to the page.
Visualizing drag without duplicate paths
Section titled “Visualizing drag without duplicate paths”For drag demos, avoid moving the overlay pointer to the destination before the drag starts. If you do, users will see one preview path and then the real drag path, which looks like duplicate animation.
Instead, move to the drag start, begin drag, then advance both the real drag and overlay pointer using the same stepped coordinates.
const from = { x: 160, y: 220 };const to = { x: 420, y: 220 };const steps = 30;
await pointer.moveTo(from.x, from.y, { durationMs: 450, steps: 18, easing: "linear" });await view.dragStartAt(from.x, from.y);
for (let i = 1; i <= steps; i++) { const x = Math.round(from.x + ((to.x - from.x) * i) / steps); const y = Math.round(from.y + ((to.y - from.y) * i) / steps);
await Promise.all([ view.dragMoveTo(x, y, { steps: 1, durationMs: 28 }), pointer.moveTo(x, y, { steps: 1, durationMs: 28, easing: "linear" }), ]);}
await view.dragReleaseAt(to.x, to.y);Useful options
Section titled “Useful options”| Option | Type | What it changes |
|---|---|---|
color | string | Overlay cursor color. |
size | number | Cursor size in pixels. |
withLabel | boolean | Shows coordinate label beside the cursor. |
trackPageMouseMoves | boolean | Emits page-originated mouse move events back to the host. |
Visual checkpoints
Section titled “Visual checkpoints”Use a screenshot right after a meaningful state change:
await view.waitForElement(".toast-success", { strategy: "observer", timeoutMs: 5000 });const image = await view.captureViewport({ format: "png", encoding: "base64" });console.log("Screenshot captured", String(image).length);When to use visualization
Section titled “When to use visualization”- Debugging click offsets or hover targets.
- Verifying pointer movement in demos and docs.
- Collecting proof for flaky UI behavior.
- Comparing rendered state across runs.
Cleanup
Section titled “Cleanup”Always stop overlays and release the browser when done.
await pointer.stop();await view.close();