A Google Chrome (Manifest V3) extension that detects AI-generated images entirely on
your device — WebGPU/WASM inference via onnxruntime-web, no cloud services, no local
servers, no telemetry. Every analyzed image gets a visible badge (AI 87% /
Real 92%) and machine-readable attributes with a confidence score.
Measured performance: 88.3% balanced accuracy (WebGPU tier; 87.6% on the CPU/WASM tier) at the fixed 0.65 confidence threshold on a 9,394-image held-out validation set spanning 43 generators (including gpt-image-1.5/2, Midjourney v7, FLUX.2, Ideogram, Firefly 3) and 6 real-photo sources, with web-realistic JPEG/resize/WebP laundering applied to 70% of images. Full per-tier/per-generator tables: docs/results/; methodology: eval/.
Model weights (one-time download at setup): https://huggingface.co/Psydoc/veridict-models
- A content script finds
<img>elements (≥64px), prioritizes visible ones, and overlays a badge on each analyzed image. It also sets machine-readable attributes on the<img>:data-aidet-score(AI probability, 0–1),data-aidet-verdict(ai|realat the 0.65 threshold),data-aidet-status,data-aidet-source. - The background service worker routes requests to an offscreen document — the
inference host. It fetches image bytes (host permissions make this CORS-exempt, so
the page's own images are re-fetched, never screenshotted), then:
- runs a provenance layer first: PNG/JPEG/WebP metadata walkers detecting
generator signatures (A1111, ComfyUI, Midjourney, C2PA content credentials, IPTC
trainedAlgorithmicMedia, NovelAI stealth alpha-LSB). A hard metadata hit short-circuits with score ≥0.98 (measured zero false fires on 4,697 real photos); - otherwise decodes and runs a 5-model ONNX ensemble, gated by an early-exit cascade (a fine-tuned first-stage model answers ~98% of images alone at ~0.7s; uncertain images escalate to the full committee): Community Forensics ViT-S/384 (MIT) — stock plus a laundering-hardened fine-tune on OpenFake-train/CF-Small/COCO (training scripts in models/finetune/), GRIP-UNINA clipdet CLIP ViT-L/14 (Apache-2.0), xRayon ConvNeXtV2 (MIT), and SAFE (Apache-2.0);
- fuses member logits with quality-conditioned fitted stacking (JPEG-quality and resolution features modulate member trust) calibrated so the balanced-accuracy-optimal operating point sits exactly at 0.65 displayed confidence.
- runs a provenance layer first: PNG/JPEG/WebP metadata walkers detecting
generator signatures (A1111, ComfyUI, Midjourney, C2PA content credentials, IPTC
- Inference runs on WebGPU (fp16, in a dedicated worker) when available, falling back to multithreaded WASM (int8) otherwise. Preprocessing is a bit-exact TypeScript port of Pillow's resampling, verified against the Python reference by fixture tests.
- Model weights are downloaded once during setup (SHA-256-verified against hashes pinned in the extension package) into browser-private storage (OPFS). After that the extension is fully offline. Verdicts are cached by content hash.
Requires Node.js ≥ 20 (no Python needed to build the extension).
npm ci
npm run buildThe loadable extension is produced at extension/dist.
- Open
chrome://extensions, enable Developer mode (Chrome ≥ 124). - Click Load unpacked and select the
extension/distdirectory. - The setup page opens automatically: click Download models (one-time; ~985 MB on WebGPU machines, ~490 MB on CPU-only machines). After it completes, the extension needs no network access ever again.
- Browse. Analyzed images get badges; click the toolbar icon for a per-page summary.
To analyze file:// pages, additionally enable "Allow access to file URLs" for the
extension in chrome://extensions.
npm run typecheck
npm test # preprocessing parity (bit-exact vs Pillow), metadata rules, ONNX logit parity
node scripts/smoke-e2e.mjs # end-to-end in headless Chrome (asserts real model inference)
HEADED=1 node scripts/smoke-e2e.mjs # same, headed (exercises the WebGPU tier)Everything is reproducible from source; see:
models/— Python scripts that convert the upstream checkpoints to ONNX (fp16/int8), verify numerical parity (fp32 ≤1e-4, fp16 ≤5e-3 probability deviation), and emit the golden fixtures used by the JS parity tests.models/upload_hf.pypublishes weights + the pinned-hash download manifest.eval/— validation dataset assembly (13 public sources, seeded, quota-bounded, resume-safe), the T0–T5 web-realistic corruption protocol, scoring harnesses (score_torch.pyfor fast Metal scoring,score.tsfor deployment-exact ONNX scoring through the same shared TypeScript preprocessing), andcalibrate.py, which fits the stacking + calibration and writesshared/src/calibration.json.shared/— preprocessing, ensemble/calibration, and metadata modules imported by both the extension and the eval harness, so what is measured is what ships.
Python tooling (model conversion / eval only): uv venv .venv --python 3.12 and
install eval/requirements.txt.
Image bytes never leave the browser. There are no analytics, no remote calls after setup, and the one-time model download contacts only the public model repository.
MIT — see LICENSE. Model weights are downloaded at setup from their public
repositories and remain under their upstream licenses (MIT / Apache-2.0; every
redistributed file is listed with license and SHA-256 in models/out/*.manifest.json).