About this demo
Bot Arena is a deliberately-obvious target for browser automation. It has 13 sign-in pages organised into two sections — every page is built so a stock Playwright test (getByLabel('Email').fill(...), click Sign in, expect "Access granted") fails for a different, named reason.
The site does not run any automation itself. It is designed to be a target you point your own tool at. The Detection Log panel on every page shows, in real time, every signal the page captured — passed signals in green, failed signals in red — so you can see exactly which checks your tool tripped.
The two sections
Section 1 — Bot detection (5 levels)
The site detects automation through fingerprinting, behavioural signals, or third-party challenge. Levels:
- The honest tell — webdriver flags, plugins, UA hints.
- CDP attached — browser-chrome shape, missing Chrome surface, taskbar.
- Mouse trajectory — mousemove density and curvature.
- Fingerprint battery — canvas, audio, WebGL, fonts.
- Cloudflare Turnstile — real managed-mode widget.
Section 2 — Selector resistance (8 levels)
The DOM that selector-based automation depends on is absent, randomised, sealed, in a different frame, or unreachable without vision. Levels:
- Canvas-rendered login — no DOM form; just pixels.
- Dynamic selectors — every id/name/class rerolls per request.
- Closed Shadow DOM — form sealed inside a Web Component.
- Iframe-embedded form — form in a child frame.
- Slider verification — drag-to-align CAPTCHA.
- Image-only labels — every label rendered as a graphic.
- Cross-origin iframe — Stripe/Auth0-style isolation.
- Virtual scrolling — 1,000-row list, off-screen rows not in DOM.
Reading the Detection Log
- PASS — the signal is consistent with a real human in a real browser.
- FAIL — the signal is consistent with automation, a headless browser, or DOM-resistant rendering.
- INFO — a contextual note that does not affect the verdict (e.g., "Turnstile widget mode: interactive", or "Trajectory recorder armed").
- The verdict pill at the top of the log aggregates: any FAIL turns it red.
The failure report
The failure report at /report is a static dashboard with one card per level. Each card includes:
- The exact Playwright code that runs (one shared shape per test).
- The error Playwright surfaces when it fails — the same message you would see in your own CI.
- A plain-English explanation of the underlying problem and why a real-browser-over-VNC approach sidesteps it.
- A Playwright context block: what would it take to make this test pass in Playwright? With a 1–5 difficulty meter and the verdict (often: impossible, or maintenance nightmare).
- An AIVA context block: does AIVA currently pass this level? If not, what specific configuration or code change closes the gap? Also with a 1–5 complexity meter.
- The Detection Log signals that caused the failure.
Running the test suite
A Playwright test suite at playwright/levels.spec.ts in the repo runs one sign-in test per level against the live site. All 13 tests fail by design — that is the demo. To run it locally:
git clone https://github.com/vaclavnovotny/bot-arena cd bot-arena npm install npx playwright install chromium npx playwright test
A test passing in the future means a detection technique stopped working, or browser changes lifted a selector-resistance restriction.
Hosting & stack
- Astro 5 static-first MPA, with one Preact island for the Detection Log.
- Tailwind CSS v4 via the Vite plugin.
- Cloudflare Pages for hosting, plus one Pages Function (
/api/turnstile/verify) for the Level 5 token verification. - Vitest + happy-dom for unit tests, Playwright Test for the e2e suite.
No tracking
The site stores nothing about your visit. The Detection Log lives in browser memory and resets when you reload. The one server endpoint (Level 5's Turnstile verification) calls Cloudflare's siteverify API and immediately discards the token. No analytics, no cookies beyond what Turnstile itself sets.
Source: github.com/vaclavnovotny/bot-arena — companion to the AIVA automation product at Y Soft.