Files
human_mouse/examples/demo.html
Huang Qi 65ef838bd7 feat: initial release of human_mouse v0.1.0
A small Python library for replaying real human mouse trajectories from
the SapiMouse dataset onto a Playwright page. Designed for ML-based
bot-detection research, behavioral biometrics prototyping, and
replay-based test fixtures.

Public API: load_all_segments, pick_segments, affine_warp, upsample,
replay, replay_random, download_sapimouse.

- src/ layout with hatchling build backend
- 23 pytest tests (10 transform unit + 13 integration)
- MIT license, PEP 561 py.typed marker
- python -m human_mouse download for one-shot dataset fetch
- examples/cloakbrowser_demo.py demonstrates end-to-end use with CloakBrowser

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 00:30:18 +08:00

82 lines
3.0 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>human_mouse — Trajectory Demo</title>
<style>
html, body { margin: 0; padding: 0; background: #fafafa; font-family: ui-sans-serif, system-ui, sans-serif; overflow: hidden; }
#label { position: fixed; top: 8px; left: 8px; padding: 6px 12px; background: #111; color: #fff; border-radius: 6px; font-size: 14px; z-index: 10; }
#count { position: fixed; top: 8px; right: 8px; padding: 6px 12px; background: #fff; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; z-index: 10; }
#start { position: fixed; left: 130px; top: 130px; width: 40px; height: 40px; border-radius: 50%; background: #16a34a; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; z-index: 10; }
#target { position: fixed; left: 1030px; top: 530px; width: 60px; height: 40px; background: #2563eb; color: #fff; border: none; border-radius: 6px; font-size: 14px; cursor: pointer; z-index: 10; }
canvas { position: fixed; inset: 0; }
</style>
</head>
<body>
<div id="label">mode: …</div>
<div id="count">points: 0</div>
<div id="start">start</div>
<button id="target">target</button>
<canvas id="trail"></canvas>
<script>
const params = new URLSearchParams(location.search);
const mode = params.get("mode") || "unknown";
document.getElementById("label").textContent = "mode: " + mode;
const canvas = document.getElementById("trail");
const ctx = canvas.getContext("2d");
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resize();
window.addEventListener("resize", resize);
window.__points = [];
let last = null;
let hue = 0; // degrees on the HSL wheel; 0 = red (default)
window.__setHue = (h) => { hue = h; last = null; };
window.__resetTrace = () => { window.__points = []; last = null; };
function recordPoint(x, y, t) {
const p = { x, y, t };
window.__points.push(p);
ctx.fillStyle = `hsla(${hue}, 75%, 45%, 0.85)`;
ctx.beginPath();
ctx.arc(p.x, p.y, 2, 0, Math.PI * 2);
ctx.fill();
if (last) {
ctx.strokeStyle = `hsla(${hue}, 75%, 45%, 0.45)`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(last.x, last.y);
ctx.lineTo(p.x, p.y);
ctx.stroke();
}
last = p;
}
// pointermove fires once per render frame, but getCoalescedEvents() returns
// every sub-event the browser folded into this frame — so we recover the
// full CDP event stream the driver sent us.
window.addEventListener("pointermove", (e) => {
const subs = e.getCoalescedEvents ? e.getCoalescedEvents() : [e];
const events = subs.length ? subs : [e];
for (const s of events) {
recordPoint(s.clientX, s.clientY, s.timeStamp);
}
document.getElementById("count").textContent = "points: " + window.__points.length;
}, { passive: true });
window.__clickedTarget = false;
document.getElementById("target").addEventListener("click", () => {
window.__clickedTarget = true;
});
</script>
</body>
</html>