-
-
-
Seven live browser sessions against one commit — the disagreement becomes failing Playwright specs and a pull request.
-
The engine plays both players itself. Step 7 fails: identical winning line, but only the winner is told — the ledger is the evidence.
-
Run it yourself: paste any URL, press START — a live sweep runs on Cloud Run in under a minute.
-
The evidence wall: all seven witnesses replayed in motion. A finding is never a screenshot — it is a disagreement between sessions.
-
Three live WebRTC peers in one sweep. The mute that muted nothing is caught because observers, not assertions, watched the room.
-
The deliverable: a real pull request opened by the sweep — one commit per finding, each carrying its failing Playwright spec.
-
One URL in, a pull request out: seven one-axis witnesses, Gemini as judge, failing specs as evidence — the whole loop on Cloud Run.
-
Any tile opens full-screen in the inspector — the exact settled frame that witness saw, with its axis and route on record.
Stop asking a friend to hop on a call
Every developer who has built a chat, a game, or a call knows the ritual: open two browsers, sign into two accounts, message a colleague — "can you join for a second? I need to test something." And every regression tool you might automate that with compares a page against yesterday's copy of itself, which catches what changed and never what only one kind of user sees.
Parallax is the friend who always picks up. Point it at a URL. It opens seven isolated browser contexts against the same commit at the same instant — each changing exactly one property from a baseline (role, language, theme, viewport) — and reads the disagreement. The derivation rule is fixed; the vocabulary is yours: declare any role with a rank, any BCP 47 locale (direction is derived, so a Hebrew witness gets the RTL mirror checks), any viewport — each adds one witness, never a cross-product. When the promise needs several people, it is the several people: two live players of one game, three WebRTC peers with audio genuinely travelling between them, a sender and a receiver of one message, a session that stays open while its authority is revoked.
No stored screenshots. No golden files. No selectors. The finding is the disagreement — so the first sweep of a site it has never seen still has something to say.
The whole job, autonomously (Taskmaster)
You hand it a URL and a credentials file. It:
- Finds the sign-in surface itself — on a production Arabic chat app, that was a panel with no
<form>element, rendered after hydration, beside three buttons of which two don't take credentials. It signed in as two roles anyway. - Discovers routes and controls breadth-first, honouring a written deny-list (
"delete"covers every delete button; every exclusion is logged in the feed for audit). - Decides which axes the application even supports — a monolingual app is never judged on translation; the gate says not applicable and why.
- Witnesses all seven contexts concurrently, composes a live mosaic wall, retains the CDP screencast as published motion clips.
- Judges measured-first, model-last: deterministic geometry/contrast/i18n probes decide measurements; Gemini judges only what cannot be computed.
- Plays declared protocols with its own sessions — a seven-step game verified from both boards before each next step — and listens to declared audiences: after pressing mute, who still hears the audio, and who chose silence and must not be reported?
- Emits failing Playwright specs and opens the pull request — one commit per finding. Here is a real one.
Three findings a diff cannot produce
A control that only exists on desktop — swept the-internet.herokuapp.com (nobody built it for Parallax): 26 findings, first run. Verified by hand: 20 actionable controls off-screen at 360px, zero at 768px. The page never changed; only two witnesses disagreeing can see it.
A game that ends for the winner alone — the demo fleet serves the same game at two pixel-identical routes; on one, the loser is still told it's their turn. The engine's own ledger reads: broke at step 7 of 7 — samir should have seen it but it never appeared.
A mute button that mutes nothing — a real WebRTC mesh; one route updates the button and never touches the outgoing track. Two listeners still hear her; the third muted his own speaker and is correctly not reported — the sensor measures audio energy, because a muted sender, a deafened listener and a working call all have tracks.
A detection rate means nothing without an error rate
Seven demo applications declare their own planted defects in code, including two clean controls with nothing planted — anything found there is, by definition, an error the tool made.
17 / 17 found · 0 missed · 0 false positives — graded in CI on every push 18 / 18 generated specs fail as assertions — none skipped, none passing 2,572 ms measured revocation window on an already-open session (OWASP ASVS V3 names this control; there is no standard automated verifier) 457 automated tests
Run it yourself, right now, on any public URL: perallax.mlki.app/run.html — the sweep runs on Cloud Run's background thread and the page re-attaches if you leave and come back. Finished sweeps are mirrored to Cloud Storage, so a saved run link keeps working even after the demo's single instance restarts.
Technologies
- Gemini 3.7 Flash (Vertex AI, GenAI SDK): judges the composed seven-tile wall; behind
--propose-scenarios, proposes relational/capability tests from observed evidence only — every proposal passes the same validator as hand-written declarations. - gemini-embedding-001 (Vertex): semantic equivalence of changed text, cosine ≥ 0.90 — replaced a hash comparison that scored correct and wrong translations in overlapping bands.
- Cloud Translation v2: translates the baseline into the variant locale so comparison is same-language.
- Gemma 4 26B A4B (Vertex, or self-hosted via
PARALLAX_GEMMA_URL): names the shared cause behind repeated findings — a language judgement, never a measurement. - Cloud Text-to-Speech (Chirp3-HD): narrates the demo film, aligned to timestamps the recorder observed.
- Cloud Run (us-central1, one instance): serves console + sweep API; Cloud Build → Artifact Registry deploys it. Python/Playwright/Chromium underneath; specs emitted as TypeScript.
- Cloud Storage: every finished run is mirrored file by file through the runtime service account — status, feed, mosaics, and specs are read back from the mirror by instances that never ran them.
Findings & learnings
- The gate that grades you must be un-gameable by you. My arena fixture broke at step 2 for an unrelated reason and the gate still counted its step-7 plant as found — kind+axis+route wasn't identity. Plants can now require a phrase their finding must contain.
- Prose is not structure. The axis gate once believed our own console had a language switcher because a finding card mentioned Arabic. Controls are switchers by their short labels, not by sentences about them.
- CDP screencasts were being thrown away after deciding settle-time. They now ship as per-surface motion WebPs — and Gemini accepts video natively, so a temporal lens (hover, scroll, animation jank) is the designed next step: the footage ships today, the lens doesn't yet. Every limit like this is written in the README, not implied away.
- The demo must be the product. The film is one uncut browser session: a URL pasted into the deployed service on camera, findings produced live, and the pull request opened by the sweep — because a screenshot of a pull request instead of the pull request would be exactly the substitution this project argues against.
Individual entry. Everything above is reproducible from the repo README (parallax.toml + one command), and the graded figures are produced by the same workflow CI runs on every push.
Built With
- artifact-registry
- chromium
- cloud-build
- cloud-run
- cloud-translation
- gemini
- gemma
- google-genai-sdk
- playwright
- python
- text-to-speech
- typescript
- vertex-ai
- webrtc