@airoast

AI roast

import React, { useState, useRef, useCallback } from "react"; const RED_FLAGS = [ "Mirror selfie in the bathroom — we can see the toilet, and so can everyone else.", "Every photo is a group photo. Nobody knows which one you are, and honestly neither do we.", "Fish. Dead fish. Held up like a trophy. We get it, you have a permit.", "Gym mirror flex with the phone covering half your face. Confidence, meet camouflage.", "Sunglasses in literally every single photo. What are you hiding, the FBI or your eyes?", "A car you're leaning on that isn't yours. The rental agency knows. We know.", "Snapchat dog filter as your only close-up. It's giving 'avoiding eye contact with the camera.'", "Ex visibly cropped out of three different photos. The elbow in frame 2 is a whole subplot.", "Full slideshow of a vacation with zero photos of your actual face.", "Bio is just song lyrics with no punctuation. We're not decoding that.", "A quote about 'red flags' as a joke, which is itself the red flag.", "Six photos, six different filters, zero photos of what you actually look like in daylight.", ] const IMPRESSIONS = [ "Trying way too hard to look like they're not trying.", "Has a group chat that peaked in 2019 and never left.", "Definitely says 'I'm not like other people' unprompted within 10 minutes.", "Owns at least one canvas print of a sunset they did not take.", "Will send a paragraph text and then go silent for four days.", "Has strong opinions about pineapple on pizza and will share them immediately.", "Peaked in the group photo, not in real life.", "The kind of person whose Spotify Wrapped is their whole personality.", "Confidently wrong about at least one conspiracy theory.", "Has never posted a photo where they're actually laughing, only performing a laugh.", ] const FIXES = [ "Lead with a solo photo where we can actually see your face and both eyes.", "Delete the mirror selfie. All of them. Yes, even that one.", "Add one photo that shows an actual hobby, not just you holding a drink at golden hour.", "Crop your ex out properly, or better, just retake the photo.", "Swap the sunglasses shot for literally anything showing your eyes.", "Write a bio that says something specific about you, not a Pinterest quote.", "Get a friend to take one non-selfie photo of you existing in the world.", "Cut it down to 4–6 strong photos. Nobody is scrolling through eleven vacation shots.", "Add a photo that hints at a personality trait, not just a vibe.", "Smile like you mean it in at least one shot. The smize isn't landing.", ] const VERDICTS = [ { min: 0, max: 25, label: "Certified disaster", tone: "Someone swiped left out of pity." }, { min: 26, max: 45, label: "Needs a rescue mission", tone: "Salvageable, but it's going to take work." }, { min: 46, max: 64, label: "Mid, and not in a good way", tone: "You're the human embodiment of a shrug emoji." }, { min: 65, max: 79, label: "Actually kind of solid", tone: "A few fixes from genuinely dangerous." }, { min: 80, max: 92, label: "Certified heartbreaker", tone: "Rude, honestly. Save some for the rest of us." }, { min: 93, max: 100, label: "Unfair advantage", tone: "This should require a permit." }, ] function hashSeed(str) { let h = 0; for (let i = 0; i < str.length; i++) { h = (h * 31 + str.charCodeAt(i)) >>> 0; } return h; } function mulberry32(seed) { return function () { seed |= 0; seed = (seed + 0x6d2b79f5) | 0; let t = Math.imul(seed ^ (seed >>> 15), 1 | seed); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; } function pick(rng, arr, n = 1) { const pool = [...arr]; const out = []; for (let i = 0; i < n && pool.length; i++) { const idx = Math.floor(rng() * pool.length); out.push(pool.splice(idx, 1)[0]); } return n === 1 ? out[0] : out; } function getVerdict(score) { return VERDICTS.find((v) => score >= v.min && score <= v.max) || VERDICTS[2]; } function generateRoast(seedString) { const rng = mulberry32(hashSeed(seedString + Date.now().toString().slice(-4))); const score = Math.floor(rng() * 100) + 1; const redFlag = pick(rng, RED_FLAGS); const impression = pick(rng, IMPRESSIONS); const fixes = pick(rng, FIXES, 3); const verdict = getVerdict(score); return { score, redFlag, impression, fixes, verdict }; } const STAGE = { UPLOAD: "upload", ANALYZING: "analyzing", RESULT: "result", PAYWALL: "paywall", }; function ScoreDial({ score }) { const circumference = 2 * Math.PI * 54; const offset = circumference - (score / 100) * circumference; const color = score >= 80 ? "#3ddc84" : score >= 50 ? "#ffcf3d" : score >= 26 ? "#ff8a3d" : "#ff4d4d"; return ( <div style={{ position: "relative", width: 140, height: 140, margin: "0 auto" }}> <svg width="140" height="140" viewBox="0 0 120 120"> <circle cx="60" cy="60" r="54" fill="none" stroke="rgba(255,255,255,0.08)" strokeWidth="10" /> <circle cx="60" cy="60" r="54" fill="none" stroke={color} strokeWidth="10" strokeLinecap="round" strokeDasharray={circumference} strokeDashoffset={offset} transform="rotate(-90 60 60)" style={{ transition: "stroke-dashoffset 1.1s cubic-bezier(.2,.9,.3,1), stroke 0.4s" }} /> </svg> <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", }} > <div style={{ fontFamily: "'Archivo Black', sans-serif",

No posts yet.

© 2026 AI roastPowered by Result