const PROBES = [ { id: "feed-freeze", code: "01", title: "Feed Freeze", blurb: "Stop mid-scroll when the real cue hits.", trait: "doomscroll impulse", nextHint: "No compass. Landmarks only.", }, { id: "landmark", code: "02", title: "Landmark Labyrinth", blurb: "Navigate a block with street cues — no arrow.", trait: "GPS dependence", nextHint: "Memorize a scene. Spot the swaps.", }, { id: "scene-swap", code: "03", title: "Scene Swap", blurb: "Glance, then tap every change.", trait: "glance memory", nextHint: "Write past the autocomplete.", }, { id: "anti-complete", code: "04", title: "Anti-Complete", blurb: "Finish the line unlike a predictive model.", trait: "autocomplete gravity", nextHint: "Hold one question through three hops.", }, { id: "deep-link", code: "05", title: "Deep Link Amnesia", blurb: "Survive the rabbit hole. Recall the start.", trait: "context thrash", nextHint: null, }, ]; const SPEED_SECONDS = 300; const RUN_ORDER = PROBES.map((p) => p.id); const FEED_POSTS = [ { handle: "@voidscroll", body: "unskippable lore drop about nothing in particular" }, { handle: "@dopamine.inc", body: "you won’t believe what happens at 0:07" }, { handle: "@groupchat", body: "replying “lol” to keep the thread alive" }, { handle: "@algo", body: "recommended because you paused for 0.4s once" }, { handle: "@muted", body: "a calm photo of a plant. you scroll past it." }, { handle: "@thread", body: "1/47 a nuanced take that will not stay nuanced" }, { handle: "@notify", body: "someone liked your like of a like" }, { handle: "@clip", body: "same joke, third platform, zero context" }, { handle: "@live", body: "going live to ask if anyone is awake" }, { handle: "@ads", body: "shop the exact hoodie from the crashout" }, { handle: "@recap", body: "your year in review arrived in July" }, { handle: "@brain", body: "hot take that agrees with you too hard" }, ]; const BAIT_BODIES = [ "BREAKING: tap now or stay mid", "limited drop — 2 left in your size", "your ex viewed your story (maybe)", "this will ruin your whole week", ]; const LANDMARKS = { "1,1": "blue café", "1,3": "bike rack", "2,2": "old oak", "3,1": "laundromat", "3,3": "neon diner", }; const AC_PROMPTS = [ { stem: "The best way to spend a Sunday is", suggestions: ["relaxing at home", "catching up on sleep", "hanging with friends"], predicted: [ "relaxing", "at", "home", "sleeping", "in", "bed", "watching", "netflix", "with", "friends", "family", "catching", "up", "on", "rest", "doing", "nothing", "chilling", "brunch", "coffee", ], }, { stem: "When my phone dies I usually", suggestions: ["feel anxious", "look for a charger", "borrow someone’s"], predicted: [ "feel", "anxious", "panic", "look", "for", "a", "charger", "plug", "it", "in", "ask", "someone", "borrow", "get", "nervous", "freak", "out", "wait", "until", "home", ], }, { stem: "A perfect group chat message is", suggestions: ["short and funny", "a meme", "just lol"], predicted: [ "short", "and", "funny", "a", "meme", "gif", "lol", "lmao", "relatable", "quick", "reply", "something", "witty", "joke", "react", "emoji", "just", "yes", "ok", "sure", ], }, ]; const DEEP_LINKS = [ { question: "What year was the Eiffel Tower completed?", start: { title: "Eiffel Tower — overview", body: "Iron lattice tower on the Champ de Mars in Paris. Built as the entrance arch for the 1889 World’s Fair.", links: [ { label: "Construction timeline →", next: 1 }, { label: "Gustave Eiffel bio →", next: 2 }, { label: "Other Paris landmarks →", next: 3 }, ], }, pages: [ null, { title: "Construction timeline", body: "Work began in 1887. Rivets, wrought iron, and a lot of arguing with Parisians who hated the look.", links: [ { label: "1889 World’s Fair exhibits →", next: 4 }, { label: "Workers & safety →", next: 5 }, { label: "Rival tower proposals →", next: 3 }, ], }, { title: "Gustave Eiffel", body: "Engineer behind bridges, the Statue of Liberty’s internal frame, and one very tall argument.", links: [ { label: "Statue of Liberty frame →", next: 4 }, { label: "His other bridges →", next: 5 }, { label: "Back to tower facts →", next: 1 }, ], }, { title: "Other Paris landmarks", body: "Louvre, Notre-Dame, Sacré-Cœur — none of them are the thing you were asked about.", links: [ { label: "Louvre pyramid drama →", next: 5 }, { label: "Notre-Dame rebuild →", next: 4 }, { label: "Return to Eiffel notes →", next: 1 }, ], }, { title: "World’s Fair rabbit hole", body: "Exhibits included colonial pavilions, new electric lights, and souvenirs nobody needed.", links: [ { label: "Electric lighting craze →", next: 5 }, { label: "Fair attendance myths →", next: 3 }, { label: "One more hop →", next: 5 }, ], }, { title: "You are deep now", body: "Something about rivets, tourism, and a gift shop. The original question is getting soggy.", links: [], }, ], choices: [ "What year was the Eiffel Tower completed?", "Who designed the Louvre pyramid?", "When did Notre-Dame catch fire?", "How tall is the Statue of Liberty?", ], answer: 0, }, { question: "Which planet has the most moons?", start: { title: "Solar system cheat sheet", body: "Eight planets, countless rocks, and a comment section arguing about Pluto.", links: [ { label: "Gas giants compared →", next: 1 }, { label: "Moon definition drama →", next: 2 }, { label: "Pluto discourse →", next: 3 }, ], }, pages: [ null, { title: "Gas giants", body: "Jupiter and Saturn trade the moon crown depending on which year the catalogs update.", links: [ { label: "Saturn’s rings & moons →", next: 4 }, { label: "Jupiter’s big four →", next: 5 }, { label: "Uranus joins chat →", next: 3 }, ], }, { title: "What counts as a moon?", body: "Capture objects, temporary moons, and telescopes finding new pebbles every season.", links: [ { label: "Temporary moons →", next: 4 }, { label: "Naming conventions →", next: 5 }, { label: "Back to giants →", next: 1 }, ], }, { title: "Pluto discourse", body: "Not a planet (officially). Still has moons. Still starts fights at dinner.", links: [ { label: "Dwarf planet list →", next: 5 }, { label: "Kuiper belt tour →", next: 4 }, { label: "Gas giants again →", next: 1 }, ], }, { title: "Catalog update spiral", body: "New tiny moons announced. Press releases. Someone edits Wikipedia at 2am.", links: [ { label: "Telescope gossip →", next: 5 }, { label: "Naming petitions →", next: 3 }, { label: "Final hop →", next: 5 }, ], }, { title: "Orbiting the point", body: "You’ve collected trivia. The starting question is somewhere behind you.", links: [], }, ], choices: [ "Which planet has the most moons?", "Is Pluto a planet?", "How many rings does Saturn have?", "What is the Kuiper belt?", ], answer: 0, }, ]; const state = { mode: null, screen: "home", lastProbe: null, lastResult: null, traitScores: loadTraits(), run: null, }; const $ = (sel, root = document) => root.querySelector(sel); const $$ = (sel, root = document) => [...root.querySelectorAll(sel)]; const clamp = (n, a, b) => Math.max(a, Math.min(b, n)); const avg = (arr) => (arr.length ? arr.reduce((s, n) => s + n, 0) / arr.length : 0); function loadTraits() { try { return JSON.parse(localStorage.getItem("brainrot-traits") || "{}"); } catch { return {}; } } function saveTrait(probeId, score) { state.traitScores[probeId] = score; localStorage.setItem("brainrot-traits", JSON.stringify(state.traitScores)); } function probeById(id) { return PROBES.find((p) => p.id === id); } function showScreen(name) { state.screen = name; $$(".screen").forEach((el) => { el.classList.toggle("active", el.dataset.screen === name); }); if (name === "lab") renderLab(); if (name === "home") { stopRunClock(); setRunHud(false); animateHomeMeter(); } } function animateHomeMeter() { const fill = $("#homeMeter"); if (!fill) return; fill.style.width = "8%"; requestAnimationFrame(() => { fill.style.width = `${28 + Math.random() * 40}%`; }); } function stampGrade(score) { if (score < 35) return { text: "INTACT", grade: "grade-intact" }; if (score < 60) return { text: "MILD", grade: "grade-mild" }; return { text: "FRIED", grade: "grade-fried" }; } function applyStamp(score) { const el = $("#resultStamp"); if (!el) return; const g = stampGrade(score); el.textContent = g.text; el.className = `stamp ${g.grade}`; requestAnimationFrame(() => { el.classList.add("show"); }); } function setRunHud(on) { $("#runHud").hidden = !on; $("#app").classList.toggle("run-active", on); if (on && state.run) updateRunHud(); } function updateRunHud() { if (!state.run) return; const done = state.run.results.length; $("#runProbeLabel").textContent = `Probe ${Math.min(done + 1, RUN_ORDER.length)} of ${RUN_ORDER.length}`; const left = Math.max(0, state.run.endsAt - performance.now()); const m = Math.floor(left / 60000); const s = Math.floor((left % 60000) / 1000); $("#runClock").textContent = `${m}:${String(s).padStart(2, "0")}`; $("#runProgress").style.width = `${((SPEED_SECONDS * 1000 - left) / (SPEED_SECONDS * 1000)) * 100}%`; } function stopRunClock() { if (state.run?.clockId) clearInterval(state.run.clockId); } function startSpeedRun() { abortActiveProbe(); state.mode = "run"; state.run = { results: [], index: 0, endsAt: performance.now() + SPEED_SECONDS * 1000, clockId: null, timedOut: false, }; setRunHud(true); state.run.clockId = setInterval(() => { updateRunHud(); if (performance.now() >= state.run.endsAt) { state.run.timedOut = true; finishSpeedRun(); } }, 200); launchProbe(RUN_ORDER[0], { skipReady: false }); } function startLabProbe(id) { abortActiveProbe(); state.mode = "lab"; state.run = null; setRunHud(false); launchProbe(id); } function launchProbe(id, opts = {}) { state.lastProbe = id; const starters = { "feed-freeze": () => resetFeedFreeze(opts), landmark: () => resetLandmark(), "scene-swap": () => resetSceneSwap(opts), "anti-complete": () => resetAntiComplete(), "deep-link": () => resetDeepLink(), }; starters[id]?.(); showScreen(id); } function abortActiveProbe() { ff.running = false; clearInterval(ff.timerId); cancelAnimationFrame(ss.timerId); clearTimeout(ss.memorizeId); } function completeProbe(result) { state.lastResult = result; saveTrait(result.probeId, result.score); if (state.mode === "run") { state.run.results.push(result); const next = state.run.index + 1; if (state.run.timedOut || next >= RUN_ORDER.length) { finishSpeedRun(); return; } state.run.index = next; showInterstitial(result, probeById(RUN_ORDER[next])); return; } showProbeResult(result); } function showInterstitial(done, next) { $("#ixEyebrow").textContent = `${done.title} · ${done.score} pts`; $("#ixTitle").textContent = next.title; $("#ixBlurb").textContent = done.nextHint || next.blurb; $("#ixMeter").style.width = `${(state.run.results.length / RUN_ORDER.length) * 100}%`; showScreen("interstitial"); setTimeout(() => { if (state.mode !== "run" || state.run?.timedOut) return; launchProbe(next.id, { skipReady: true }); }, 1400); } function finishSpeedRun() { stopRunClock(); abortActiveProbe(); const results = state.run?.results || []; const completed = results.length; const scores = results.map((r) => r.score); const base = avg(scores); const incompletePenalty = (RUN_ORDER.length - completed) * 12; const timeBonus = completed === RUN_ORDER.length && !state.run.timedOut ? -8 : state.run.timedOut ? 6 : 0; const score = Math.round(clamp(base + incompletePenalty + timeBonus, 0, 99)); const blurb = completed === 0 ? "Time’s up before a single probe landed. The clinic has notes." : completed < RUN_ORDER.length ? `You cleared ${completed}/${RUN_ORDER.length} before the clock cooked you.` : score < 35 ? "Full circuit. Attention mostly intact. Suspicious." : score < 60 ? "Full circuit with noticeable rot. Hydrate and touch grass." : "Full circuit, heavily fried. Your thumbs filed a complaint."; setRunHud(false); state.mode = "run-done"; showCompositeResult({ title: "Speed run", label: "brainrot score", score, blurb, results, completed, }); } function showProbeResult(r) { $("#resultEyebrow").textContent = "probe result"; $("#resultTitle").textContent = r.title; $("#resultLabel").textContent = r.label; $("#resultScore").textContent = String(r.score); $("#resultUnit").textContent = "brainrot pts"; $("#resultBlurb").textContent = r.blurb; $("#resultBreakdown").hidden = true; $("#againBtn").textContent = "Retry probe"; $("#resultSecondary").textContent = "Back to lab"; $("#resultMeter").style.width = "0%"; $("#resultStamp").className = "stamp"; showScreen("result"); requestAnimationFrame(() => { $("#resultMeter").style.width = `${clamp(r.score, 0, 100)}%`; applyStamp(r.score); }); } function showCompositeResult({ title, label, score, blurb, results }) { $("#resultEyebrow").textContent = "clinic summary"; $("#resultTitle").textContent = title; $("#resultLabel").textContent = label; $("#resultScore").textContent = String(score); $("#resultUnit").textContent = "brainrot score"; $("#resultBlurb").textContent = blurb; const box = $("#resultBreakdown"); box.hidden = false; box.innerHTML = RUN_ORDER.map((id) => { const probe = probeById(id); const hit = results.find((r) => r.probeId === id); return `
${page.body}
`; const actions = $("#dlActions"); actions.innerHTML = ""; if (dl.hops >= 3) { $("#dlBrief").textContent = "What was your original question?"; $("#dlHop").textContent = "3/3"; dl.pack.choices.forEach((label, i) => { const btn = document.createElement("button"); btn.type = "button"; btn.className = "link-choice"; btn.textContent = label; btn.addEventListener("click", () => endDeepLink(i)); actions.appendChild(btn); }); return; } page.links.forEach((link) => { const btn = document.createElement("button"); btn.type = "button"; btn.className = "link-choice"; btn.textContent = link.label; btn.addEventListener("click", () => { dl.hops += 1; $("#dlHop").textContent = `${dl.hops}/3`; const next = dl.pack.pages[link.next] || dl.pack.pages[5]; renderDeepPage(next); }); actions.appendChild(btn); }); } function endDeepLink(choice) { const correct = choice === dl.pack.answer; const score = correct ? 18 : 82; const blurb = correct ? "Held the thread. Attention residue didn’t eat the brief." : "Lost the starting question in the hops. Classic deep-link amnesia."; completeProbe({ probeId: "deep-link", title: "Deep Link Amnesia", label: "context thrash", score, blurb, nextHint: null, }); } /* ——— Wire UI ——— */ function wireUi() { $("#speedRunBtn").addEventListener("click", startSpeedRun); $("#labBtn").addEventListener("click", () => { state.mode = "lab"; showScreen("lab"); }); $$(".exit-probe").forEach((btn) => btn.addEventListener("click", exitProbe)); $$("[data-nav]").forEach((btn) => { btn.addEventListener("click", () => { abortActiveProbe(); stopRunClock(); state.run = null; setRunHud(false); showScreen(btn.dataset.nav); }); }); $("#ffReady").addEventListener("click", beginFeedFreeze); $("#ffFreeze").addEventListener("click", () => endFeedFreeze("freeze")); $("#lmLeft").addEventListener("click", () => lmTurn(-1)); $("#lmRight").addEventListener("click", () => lmTurn(1)); $("#lmForward").addEventListener("click", lmForward); $("#ssReady").addEventListener("click", beginSceneSwap); $("#ssDone").addEventListener("click", endSceneSwap); $("#acSubmit").addEventListener("click", endAntiComplete); $("#againBtn").addEventListener("click", () => { if (state.mode === "run-done" || $("#againBtn").textContent.includes("Run")) { startSpeedRun(); return; } startLabProbe(state.lastProbe); }); $("#resultSecondary").addEventListener("click", () => { state.mode = "lab"; setRunHud(false); showScreen("lab"); }); $("#resultBack").addEventListener("click", () => { setRunHud(false); showScreen(state.mode === "run-done" ? "home" : "lab"); }); } wireUi(); animateHomeMeter();