import React, { useEffect, useRef, useState } from "react";
import {
fetchModels, predict, fetchDatasets, sampleDataset, datasetMediaUrl,
predictDataset, scoreBatch, verdictAt, autoCalibrate, targetFprThreshold, batchStats,
sotaGenerators, sotaRealImage, sotaCompare, fetchLeaderboard, fetchGeneratorBenchmarks,
} from "./api.js";
// Which weights back the default inference checkpoint for each detector (shown in the UI).
const WEIGHTS_LABEL = {
official: "official weights",
synthscope_trained: "SynthScope-trained",
training_free: "training-free",
};
const wlabel = (m) => WEIGHTS_LABEL[m && m.weights] || "SynthScope-trained";
const REPRO_LEGEND = (
reproduced = SynthScope-trained checkpoint matches the paper · official = official weights loaded
into the SynthScope class match (SynthScope-trained shown alongside where trained from scratch) · below = gap
remains · reference = paper reports no comparable number · missing = not yet evaluated.
Every benchmark each paper reports, with the paper number, our SynthScope-trained
checkpoint, and official weights loaded into the SynthScope class. Grouped by benchmark so methods that
share one can be compared head-to-head (sorted best-first).
);
}
function TopGeneratorsView() {
const [bm, setBm] = useState([]);
const [err, setErr] = useState("");
useEffect(() => {
fetchGeneratorBenchmarks().then((d) => setBm(d.benchmarks || [])).catch((e) => setErr(e.message));
}, []);
const medal = (i) => (i === 0 ? "🥇" : i === 1 ? "🥈" : i === 2 ? "🥉" : i + 1);
return (
How our detectors hold up against today's SOTA image generators. Each: a balanced
real-vs-generated set scored by every SynthScope detector (promoted checkpoints), ranked by AUROC. None of the
detectors were trained on these generators — cross-generator generalization.
{err &&
{err}
}
{!bm.length && !err &&
No generator benchmarks yet.
}
{bm.map((b) => (
vs {b.generator} {b.n_real}+{b.n_fake} imgs · {b.settings}
{b.note}
#
Detector
AUROC
AP
acc@.5
fake-detect %
real-FP %
{b.rows.map((r, i) => (
{medal(i)}
{r.name || r.detector}
{r.auroc}
{r.ap}
{r.acc}
{r.fake_detect_rate}
{r.real_false_pos}
))}
))}
);
}
function b64ToFile(b64, name) {
const bin = atob(b64), arr = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i);
return new File([arr], name, { type: "image/png" });
}
// SOTA generator vs detector comparison: pick/upload a real image -> a current SOTA generator makes a
// "conceptually similar" image -> the selected SynthScope detectors score it (best/promoted checkpoints).
function SotaView({ models }) {
const [gens, setGens] = useState([]);
const [gen, setGen] = useState("");
const [prompt, setPrompt] = useState("make another image conceptually similar to this");
const [realB64, setRealB64] = useState("");
const [realSrc, setRealSrc] = useState(null);
const [picked, setPicked] = useState({});
const [busy, setBusy] = useState(false);
const [res, setRes] = useState(null);
const [err, setErr] = useState("");
const imgDetectors = (models || []).filter((m) => m.modality === "image" && m.available);
useEffect(() => {
sotaGenerators().then((d) => {
setGens(d.generators || []);
const a = (d.generators || []).find((g) => g.available);
if (a) setGen(a.key);
if (d.default_prompt) setPrompt(d.default_prompt);
}).catch((e) => setErr(e.message));
}, []);
async function rollImage() {
setErr(""); setRes(null);
try { const d = await sotaRealImage(); setRealB64(d.image_b64); setRealSrc(d.source); }
catch (e) { setErr(e.message); }
}
function onUpload(f) {
if (!f) return;
const rd = new FileReader();
rd.onload = () => { setRealB64(rd.result.split(",")[1]); setRealSrc(["upload", f.name]); setRes(null); };
rd.readAsDataURL(f);
}
async function run() {
setErr(""); setBusy(true); setRes(null);
try {
const dets = Object.keys(picked).filter((k) => picked[k]);
if (!realB64) throw new Error("Pick or upload a real image first.");
if (!gen) throw new Error("Select a generator.");
if (!dets.length) throw new Error("Select at least one detector.");
const d = await sotaCompare(b64ToFile(realB64, "real.png"), gen, dets, prompt);
setRes(d);
} catch (e) { setErr(e.message); } finally { setBusy(false); }
}
return (
Send a real image from our datasets to a current SOTA generator, then see whether
the SynthScope detectors catch the freshly-generated image. Generators need an API key
(greyed-out = key not set on the server).
);
}
const isVideoName = (name = "", type = "") =>
type.startsWith("video") || /\.(mp4|mov|avi|mkv|webm|m4v)$/i.test(name);
const optLabel = (m) => `${m.name} — ${m.paper} · ${wlabel(m)}${m.available ? "" : " (no checkpoint)"}`;
function ModelSelect({ models, model, setModel, onChange }) {
const img = models.filter((m) => m.modality === "image");
const vid = models.filter((m) => m.modality === "video");
return (
);
}
function ReproTable({ m }) {
const rep = m && m.repro;
const full = m && m.repro_full;
if (!rep && !(full && full.length)) return null;
return (
{rep && (
Inference weights
{wlabel(m)}
Headline
{rep.benchmark}
Paper
{rep.paper}
Reproduced
{rep.ours}
)}
{full && full.length > 0 && (
All {full.length} reported benchmarks — paper vs SynthScope vs official
Benchmark
Paper
SynthScope (trained)
Official wts
Status
{full.map((r, i) => (
{r.benchmark}
{r.paper}
{r.ours_synthscope || "—"}
{r.ours_official || "—"}
{r.status}
))}
reproduced = SynthScope-trained checkpoint matches the paper · official = official weights
loaded into the SynthScope class match (SynthScope-trained number shown where we trained from scratch) ·
below = gap remains · reference = paper reports no comparable number · missing = not yet evaluated.
)}
);
}
function ThresholdSlider({ threshold, setThreshold, children }) {
return (
Click a sample to run {(models.find((m) => m.key === model) || {}).name || "the model"} on it. Use "Score batch" + Auto-calibrate / Target FPR to set the threshold.