🛡️ Human Verification · Motion-Revealed

Phantom Motion-Revealed CAPTCHA

A verification scheme that trades recognition difficulty for the time dimension. The target shares its distribution with the background noise and emerges only through motion integration — legible to the eye, unsolvable from a single frame. It backs part of this site's human verification.

Upstream techccy/phantom (archived) MPL-2.0 Python + FastAPI backend TypeScript + Vite frontend Trajectory completeness added here 中文

Project Files

Based on techccy/phantom (upstream archived), licensed under the Mozilla Public License 2.0. All modified and added sources are listed below — click to expand.

MD
NOTICE.md 3.9 KB · License and modification notes
DIFF
trajectory-completeness.patch 5.0 KB · Backend: trajectory completeness
NEW
surface.ts 0.8 KB · Input surface → canvas mapping
NEW
pad.ts 2.1 KB · Trackpad control area and cursor ring
NEW
loupe.ts 4.9 KB · Magnifier aid
TS
phantom.ts 21.1 KB · Widget core and interaction state machine
TS
tracker.ts 3.3 KB · Trajectory capture
TS
renderer.ts 5.1 KB · Canvas rendering
TS
styles.ts 17.3 KB · Widget injected styles
TS
config.ts 4.1 KB · Tunables and runtime overrides
TS
main.ts 12.8 KB · Demo entry and tuning bench
The backend patch excludes the glue between this site and its own business systems. For unmodified upstream files, please refer to the upstream repository.

Core Mechanism

Conventional CAPTCHAs place the difficulty in recognition, which raises the cost for humans and machines alike. Phantom shifts it into the time dimension: a single frame carries zero information, and only continuous observation yields a solution.

Motion Reveal

Target particles share the same grayscale distribution as the background noise and are entirely hidden at rest. Only when the cluster translates in unison does human visual integration — the Common Fate principle — pull it out of the noise.

Behavioural Check

Recognition is only the precondition; the user must also press and follow the motion. The server judges the operator from the complete trajectory, and this is the core barrier against automation.

Zero Frontend Trust

The reference path is never transmitted. The frontend receives only a high-entropy seed; both ends derive the Bézier control points from the same deterministic PRNG, so no readable geometry ever crosses the wire.

Scoring Model

A trajectory is scored along two orthogonal dimensions, then weighted and compared against a threshold:

Composite = 0.6 · S_DTW + 0.4 · S_Bio
S_Bio     = 0.35 · energy + 0.30 · zerocross + 0.35 · tremor(amp + psd)

S_DTW · Path Fidelity

The user trajectory and the reference Bézier path are both normalised to the unit square, a 2-D dynamic time warping distance is computed, and the result is mapped to 0–1 through an exponential saturation. Normalisation decouples the score from canvas size and avoids misjudgements caused by DPR scaling.

S_Bio · Physiological Signature

The trajectory is uniformly resampled and low-passed at 7 Hz to remove voluntary motion, leaving only the residual, which is then examined for human physiological signatures:

  • Residual energy — human high-frequency micro-tremor falls in 0.3–8 px; idealised synthesis stays below 0.05 px, crude synthesis exceeds 20 px.
  • Acceleration zero-crossing rate — humans continuously correct via feedback, giving sign flips a characteristic frequency; synthetic trajectories are either over-smooth or broadband noise.
  • Physiological tremor — amplitude and power ratio in the 8–12 Hz band, matching the natural frequency of human musculature.

Hard Vetoes

Any single trigger caps the composite score at 0.20:

VetoSynthetic signature targeted
SmoothnessResidual energy near zero, typical of function-generated ideal paths
Periodic pseudo-noiseSine-injected fake tremor: non-decaying autocorrelation, single spectral peak
Arithmetic timestampsFrame-rate-stepped timestamps with a near-zero Δt coefficient of variation
Axial symmetryEqual-amplitude symmetric noise on both X and Y axes
Sub-pixel entropyCoordinate rounding collapsing the entropy of fractional digits
Minimum durationTotal elapsed time below the human physiological response floor
Endpoint proximityAdded here · released before reaching the endpoint
Duration floorAdded here · total time markedly below the expected duration

Security Design

  • Ephemeral keys — each challenge negotiates a session key over ECDH P-256; parameters and trajectories are encrypted with AES-256-GCM. The server holds no long-lived symmetric key.
  • One challenge, one answer — verification pops the challenge atomically via Redis GETDEL, so a second submission for the same challenge always fails.
  • One token, one use — a one-time HMAC token is issued on success and redeemed by the business side, again through GETDEL.
  • Reference never sent — only a 16-byte high-entropy pathSeed is transmitted; control points are derived independently at both ends. Hooking the decryption call yields nothing but the seed.
  • Freshness bound — submissions whose clock offset from the server exceeds a threshold are rejected, suppressing offline solving.

Mobile Adaptation

Upstream has the user drag directly on the canvas, where a thumb occludes the very target being followed. This deployment switches to a trackpad model:

  • The canvas above is for observation only; a control area below receives input. The two are mapped in strict proportion, with a cursor ring on the canvas indicating the current position.
  • The control area is smaller than the canvas, so scaling up also amplifies hand tremor into the physiological band — making it, in practice, easier than dragging directly.
  • The server is unaware of the change: it still receives canvas coordinates, and the scoring logic is untouched.
Coordinate mapping is consolidated into a single implementation (surface.ts) shared by trajectory capture and the cursor ring, keeping the two permanently consistent.

Completeness Check

Upstream scoring concentrates on path fidelity and says little about completion: releasing before the trajectory finishes may still pass. A completeness check was added here on top of that.

Areas that admit reinforcement

  1. Endpoint position takes no part in the decision. The target radius is used only for frontend drawing; scoring is limited to path fidelity and physiological signature, and never considers the final position.
  2. DTW is insensitive to truncation. After an early release the remaining reference segment aligns onto the last sample. That cost is proportional to the remaining length, but little survives sequence normalisation and exponential saturation.
  3. Challenge duration never reaches scoring. The duration is written to storage at challenge creation but is not referenced during scoring, so the expected duration plays no part in the decision.
  4. The existing minimum-duration floor of 250 ms offers limited constraint in this scenario.

Measured before and after (production parameters)

Path followedReleased early byPass rate beforeAfter
100%100%100%
80%1.4 s100%0%
70%2.1 s100%0%
55%3.1 s100%0%
50%3.5 s83%0%

That is, following roughly 55% of the path sufficed — equivalent to releasing a 7-second challenge at 3.9 seconds.

Approach

Two symmetric hard vetoes were added:

  • Endpoint proximity — the distance from the last sample to the path endpoint must not exceed twice the target half-extent.
  • Duration floor — total elapsed time must be at least 80% of the expected duration.
Thresholds were set from a tolerance sweep: genuine users finish a median 4.1 px from the endpoint, and even those with pronounced tremor reach only 19.6 px at p99, whereas releasing just 10% early already gives a median of 60 px — a separation of more than 2×. Measurements show no false rejections for genuine users, while submissions following ≤80% of the path all fail. Both ratios are adjustable via environment variables.

Usability and Applicability

Observations

A small-scale trial was run under the parameters adopted here, while retaining a meaningful level of difficulty. Participants were under 20 years of age and in sound physical and mental condition, with no prior exposure to comparable CAPTCHAs. Results:

  • Verification was completed within no more than 5 attempts;
  • After one successful attempt, subsequent accuracy approached 100%.
On the sample. The trial was a random sample of small size and carries no statistical generality; it cannot be extrapolated to the broader population. All it establishes is that, under the current parameters, the CAPTCHA remains completable within a reasonable number of attempts and has not been rendered unusable by the difficulty adjustment.

Applicability limits

The scheme requires sustained visual tracking and concurrent fine motor control, placing demands on both perception and dexterity. Applicability in the following cases remains unverified and open to improvement:

  • Older or very young users;
  • Users with limited vision, colour vision deficiency, or difficulty discriminating at low contrast;
  • Users with limited fine motor control, or relying on assistive input devices;
  • Users sensitive to motion and flicker — the canvas is a continuously changing noise field.

The scheme also offers no accessible alternative path: its content cannot be parsed by screen readers, and there is no audio or other non-visual channel. On low-frame-rate devices or high-latency networks the following experience degrades noticeably. Its general applicability is therefore limited, and it should not serve as the sole verification entry point.

Known risk

Adjudication happens entirely server-side and the frontend cannot influence the outcome. Rendering, however, happens on the client, and this entails an inherent concession: a user may raise the target particles' brightness gain in the frontend so the target becomes identifiable within a single frame, then infer the path from the highlighted region. Doing so weakens the visual defence and carries a very low probability of circumvention.

The behavioural defence is unaffected — even with full knowledge of the target's position, one must still construct a trajectory that simultaneously satisfies path fidelity, the physiological checks, and every hard veto. It should nonetheless be stated plainly: the visual protection of motion reveal is, for anyone willing to modify the frontend, close to non-existent.

Deployment guidance

This scheme is not recommended as a sole security boundary. It belongs inside a tiered verification system:

  • Risk-based tiering — have a risk-control system score the source and match verification strength to the risk tier: pass low risk through, escalate high risk.
  • Multiple forms in parallel — combine with other CAPTCHA types and dispatch by scenario and risk tier, so that no single form can be defeated in isolation.
  • Allow room to practise — first-time users need several attempts to acquire the knack, and should be granted an allowance that does not count towards their risk score. Based on the small-scale trial, this site exempts the first 10 attempts: failures in that window are advisory only and do not accumulate risk, so unfamiliarity is not mistaken for anomaly.
  • Keep a fallback — offer alternative verification or an appeal route for users who cannot complete this challenge, so it does not become a de facto barrier to entry.
  • Attempt management — once the allowance is exhausted, apply incremental backoff rather than hard lockout.
  • Server-side profiling — track cross-request trajectory similarity and feature-distribution concentration; such patterns are beyond the reach of frontend forgery.
  • Continuous observation — monitor live pass rates and failure distributions, and re-evaluate after any parameter change so that protection does not drift over time.

Integration

Load the SDK, mount it into any container, and redeem the resulting token from the backend:

<!-- 1. Load the SDK -->
<script src="/test_captcha3/phantom.js"></script>

<!-- 2. Mount the widget -->
<div id="phantom-box"></div>
<script>
  Phantom.mount("#phantom-box", {
    apiBase:   "/test_captcha3/api",
    onSuccess: (r) => console.log("token =", r.token),
    onFail:    (r) => console.warn("failed", r.detail),
  });
</script>
The token must be redeemed by the backend via /consume-token. A token held by the frontend signifies only that this step was passed and is not a business credential.

Live Demo

Press the button, follow the target with your eyes, and release once it stops. Results are also printed to the browser console.

Phantom Widget verify + score + token

Tuning Bench

Overrides are stored in this browser's localStorage and apply only to this device.

Canvas size and the pass threshold are deliberately not exposed: the former participates in DTW normalisation and the latter is the decision itself. Both live on the server. Only visual parameters are offered here.
Visual · target legibility
Challenge · following difficulty
Aids · mobile occlusion Active on mobile viewports by default; can be forced on here
Server parameters: awaiting first challenge…
Score log Adjust thresholds from observed results
No entries yet. One completed verification will appear here.

Notices

Disclaimer

Provided AS IS and AS AVAILABLE. Pursuant to MPL-2.0 §6, this software and the content of this page are provided on an “AS IS” and “AS AVAILABLE” basis, without warranty of any kind, express or implied, including but not limited to warranties of merchantability, fitness for a particular purpose, and non-infringement.

Pursuant to MPL-2.0 §7, to the maximum extent permitted by applicable law, no contributor or distributor shall be liable for any direct, indirect, incidental, special, exemplary, or consequential damages arising from the use of or inability to use this software.

On security

This project is a human-verification component and makes no guarantee of any particular level of protection. Any verification scheme may be circumvented and none should serve as a sole security boundary; deployers should assess the risk independently against their own context and accept full responsibility for deployment and use.

The upstream repository is archived; the modifications here have not been reviewed by, and do not represent, upstream.

On availability

The live demo and related endpoints on this page constitute no service commitment. They may change, be interrupted, or be withdrawn at any time, with no availability guarantee.

AI assistance

Parts of this project and its documentation were prepared with the assistance of AI tools.

AI-generated content may contain:

  • inaccurate statements
  • omissions
  • discrepancies with the actual environment

Readers should review and judge against their own circumstances.

Final project design, code implementation, and usage remain the responsibility of the user.