The click record
Public reverse-engineering work on a widely deployed challenge widget shows what its checkbox writes when it is clicked. The handler converts every value to a string with String(...) and stores at least 60 of them. They come from seven places:
- The event:
isTrusted,type,clientX/Y,screenX/Y,pageX/Y,offsetX/Y,layerX/Y,movementX/Y,buttonandbuttons, the modifier keys,detail,timeStamp, and the pointer fieldspointerId,pointerType,isPrimary,width,height,pressure,tiltX/Y,twist,altitudeAngleandazimuthAngle. - The checkbox's bounding box: x, y, left, top, right, bottom, width and height.
- The window: scroll position, inner size and
devicePixelRatio. - The document:
activeElement. - The element:
isConnected, client and offset sizes. - Computed style: display, visibility, pointer-events and opacity.
- Derived values: a touch flag, the element returned by
elementFromPoint, and the click position relative to the box, rounded down.
Many of these values have to agree with each other. The event carries several coordinate systems: client, page, screen, layer, offset and the floored relative position. Offset coordinates are rounded relative to the box while the relative ones are floored, so they can differ by one, and screen coordinates must equal client coordinates plus the iframe's position on the screen. A click whose screen coordinates are relative to the iframe instead of the screen, a known quirk of protocol-generated clicks in cross-origin frames, contradicts the window geometry.
Engine details in a click
Even a click carries the engine's signature. In captures, a WebKit tap reports a primary pointer with a random negative 32-bit pointerId, while Chromium reports pointerId 1. The checkbox's own width depends on the device's fonts, because the input covers its label: 168.125 CSS pixels in iOS Safari 26.
The same handler reads the iframe's style sheets: at most 16 sheets and 256 rules, keeping the text of every @keyframes rule. The text is the browser's own serialisation, and the engines serialise differently: WebKit writes 0%,100% without a space and adds a unit to a zero stroke-width (0px), while Chromium writes 0%, 100% and keeps 0. A browser that claims Safari and serialises CSS like Chromium is exposed by the keyframes text alone. The scan also lists body divs that have an id or inline style, which an untouched widget frame does not contain.
The lead-up: an input tally
The widget keeps a running count of seven input event types, each with its own counter, and a total that every counted event also increments: mousemove, pointermove, pointerover, touchstart, click, keydown and wheel. The counts go into the payload with the click.
Real input has a characteristic shape. A mouse produces a pointerover when it enters the frame and then a pair of pointermove and mousemove for every movement on the way to the box. A phone tap produces pointerover and touchstart, and then, when the finger lifts, a single emulated mousemove before the click, with no pointermove at all.
Automation produces its own shapes. Playwright's and Puppeteer's click() both scroll the element into view, compute its centre and call mouse.click(x, y), which moves the mouse in one step (steps defaults to 1) and then presses and releases it. The tally sees one move and a click at the geometric centre. element.click() and dispatchEvent() fire only the click, with isTrusted false and no lead-up at all.
The handler's stack and a console marker
Right after the click record, the handler writes three more values. The first is new Error().stack, created inside the click callback, which has to match the engine's stack format and the real call path; see stack traces show what called the widget. The second is a marker string that a stack getter flips from one value to another if anything reads it, which is what happens when a console or an inspector serialises the object, the technique described in timing native functions. The third is a flag for a programmatic click.
Three Date.now() marks frame the interaction: when the checkbox was set up, when it was ready, and when the proof of work started. The proof itself, a SHA-256 search over a fixed time budget with one worker per three quarters of the claimed cores, records its nonce, the number of hashes, the workers that finished and the elapsed time; see proof of work as a core-count check.
What follows for automation
A click is only convincing as part of a sequence. The pointer has to arrive from somewhere, along a path with the event density of real input, at a point that is not exactly the centre, with trusted events whose coordinates agree across every system the record stores, and on an engine whose stack, pointer ids and CSS serialisation match the claimed browser. Engine-level input, rather than synthetic events, is what produces trusted events with consistent coordinates; the path and timing are a separate problem covered in behavioural bot detection.
The through-line
The checkbox is the one moment in a challenge where the visitor does something, and the record treats it like a fingerprint of its own: dozens of values that must agree with each other, with the lead-up and with the engine. See inside a challenge widget for everything collected before the click.
