Creative Testing OS
Level Up Fight Game — Meta Ad Creative Intelligence
Phase: Day 1–3 Testing

Focus on CPM, CTR and hook rate only. Do not kill creatives before Day 3. Kill threshold active based on AOV below.

Kill threshold: $1485 (5× AOV) without a purchase
Total Pipeline
0
creatives tracked
Live in Meta
0
active
Winners
0
confirmed
In Ideation
0
pre-production
Top Paid Creatives
📊
No paid data
Upload Meta export in Paid Creatives
Organic → Paid Candidates
📱
No organic data
Upload IG export in Organic Scout
Creative Ideation
Map Avatar → Angle → Hook → Ad Type before production. 10–15 unique creatives per batch. Max 2 variations per concept.
Creative Pipeline
Status:
#Creative NameOfferAvatarAngleAd TypeHookDate LaunchedStatusResultSpendCPAHook RateCTRCPMActions
💡
No ideas yet
Add your first creative concept above
Paid Creative Performance
Upload Meta Ads Manager exports. Scored by ROAS (primary), CPA (secondary) per the testing framework. ROAS is the source of truth.
Upload Meta Export
📊
Drop Meta Ads CSV here
Ads Manager → Campaigns → Ads → Breakdown: By Ad → Export → CSV
Needs: Ad name, Amount spent, ROAS, Cost per result, CTR, CPC, CPM, Results, Reach, Impressions, Hook rate (3-sec video plays/impressions)
Creative Rankings
#CreativeScoreROAS ↕CPA ↕CTR ↕Hook Rate ↕Hold Rate ↕CPM ↕Spend ↕Results ↕Labelvs Kill
Organic → Paid Scout
Upload @tommygunna_, @levelupfightgame or @semakaddekakembo IG exports. Scored on share rate, save rate, rewatch — the metrics that translate from organic to paid.
Upload IG Content Export
📱
Drop Instagram Content CSV here
Meta Business Suite → Content → Posts & Reels → Export
Needs: Description, Views, Reach, Shares, Saves, Likes, Comments, Publish time, Post type, Permalink
Ad Creative Candidates
#ContentScoreShare Rate ↕Save Rate ↕Eng Rate ↕Rewatch ↕Views ↕Shares ↕AccountVerdictWhyLink
🔍
No organic data
Upload IG export above
Decision Rules
From the creative testing framework — when to kill, keep, and scale. All decisions are data-driven, never emotional.
Kill threshold: $1485 (5× AOV — no purchase)

Kill Threshold

AOV $297 — if an ad set spends $1485 (5× AOV) without a purchase, turn it off. AOV above $200 → 3× multiplier. AOV below $200 → 5× multiplier.

Phase Decisions
Day 1–3 — Learning Phase (DO NOT KILL)
✓ CTR above 1.5% = strong hook  |  ⚠ 0.5–1.5% = acceptable  |  ✗ Below 0.5% after $30 spend = weak
✓ CPM under $20 for MMA cold audience  |  ✗ Above $35 = audience or relevance issue
✓ Hook rate above 30% (3-sec plays/impressions)  |  ✗ Below 20% = opening frame not earning attention
Ignore ROAS, CPA at this stage. Meta's algorithm needs 50 conversion events to stabilise.
Day 3–7 — Decision Window
✓ Ad set gets a sale → keep running minimum 5 days before judging
✗ Spends 3–5× AOV without a sale → turn off
Compare each ad set ROAS to campaign average. Below average but profitable → apply spend cap, don't kill.
Attribution: 7-day click ONLY — disable 1-day view. View attribution inflates ROAS by up to 40% and optimises for fake data.
Adjust budgets twice weekly (Monday / Thursday). Max 10–30% change per adjustment.
Scaling Phase
Above ROAS target → increase budget 10–20% every 48 hours. Never jump more than 30% at once.
Frequency above 3.5 → creative fatigue incoming → rotate new batch immediately.
Each batch: 10–15 unique creatives, max 2 variations per concept — diversity beats volume.
Label every creative: Winner / Super Winner / Loser to build institutional memory.
High-spend ad sets below ROAS average → apply spend cap rather than killing.
Foundational Rules
Campaign Structure — Non-Negotiable
✓ Purchase objective only — never traffic, awareness or add-to-cart. Facebook gives you what you optimise for.
✓ Advantage+ Audience — creative messaging dictates who sees the ad, not manual targeting.
✓ All-of-funnel CBO campaign — no manual retargeting segments. Facebook handles frequency automatically.
✓ 7-day click attribution only — disable 1-day view in ad set settings → More → Attribution.
✓ Exclude existing customers — spend goes to new acquisition. Retention = email and SMS.
✓ Single CBO campaign, auto-bid (highest volume) to start. Introduce cost caps once you have data.
Ad Type Reference (from creative testing framework)
SQL + Deployment
Supabase schema for persistent storage + Cloudflare Pages deployment instructions.
Schema
RLS Policies
Cloudflare Deploy
Step 1 — Create Tables in Supabase
Supabase dashboard → SQL Editor → New Query → paste → Run
-- Level Up Creative Testing OS — Supabase Schema v1.0 CREATE TABLE creative_ideas ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), name TEXT NOT NULL, offer TEXT, landing_page TEXT DEFAULT 'levelupfightgame.com/levelupmma', avatar TEXT, angle TEXT, ad_type TEXT, hook TEXT, short_description TEXT, script TEXT, date_launched DATE, status TEXT DEFAULT 'pending' CHECK (status IN ('pending','production','ready','live','retired')), result TEXT DEFAULT 'Pending' CHECK (result IN ('Pending','Winner','Super Winner','Loser','Testing')), batch TEXT, notes TEXT ); CREATE TABLE paid_creatives ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, created_at TIMESTAMPTZ DEFAULT NOW(), imported_at TIMESTAMPTZ DEFAULT NOW(), ad_name TEXT NOT NULL, ad_id TEXT, campaign TEXT, ad_set TEXT, spend NUMERIC(10,2) DEFAULT 0, roas NUMERIC(8,4) DEFAULT 0, cpa NUMERIC(10,2) DEFAULT 0, sales INTEGER DEFAULT 0, revenue NUMERIC(10,2) DEFAULT 0, hook_rate NUMERIC(8,4) DEFAULT 0, hold_rate NUMERIC(8,4) DEFAULT 0, ctr NUMERIC(8,4) DEFAULT 0, cpm NUMERIC(10,2) DEFAULT 0, cpc NUMERIC(10,2) DEFAULT 0, results INTEGER DEFAULT 0, reach INTEGER DEFAULT 0, impressions INTEGER DEFAULT 0, score NUMERIC(8,4) DEFAULT 0, label TEXT DEFAULT 'testing' CHECK (label IN ('testing','winner','super_winner','loser','watch','paused')), date_range_start DATE, date_range_end DATE, aov_at_time NUMERIC(10,2), kill_threshold NUMERIC(10,2), notes TEXT ); CREATE TABLE organic_content ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, created_at TIMESTAMPTZ DEFAULT NOW(), imported_at TIMESTAMPTZ DEFAULT NOW(), account TEXT, description TEXT, permalink TEXT, post_type TEXT, publish_date TIMESTAMPTZ, views INTEGER DEFAULT 0, reach INTEGER DEFAULT 0, shares INTEGER DEFAULT 0, saves INTEGER DEFAULT 0, likes INTEGER DEFAULT 0, comments INTEGER DEFAULT 0, share_rate NUMERIC(10,6) DEFAULT 0, save_rate NUMERIC(10,6) DEFAULT 0, eng_rate NUMERIC(10,6) DEFAULT 0, rewatch_rate NUMERIC(8,4) DEFAULT 0, score NUMERIC(8,4) DEFAULT 0, verdict TEXT DEFAULT 'unscored' CHECK (verdict IN ('unscored','fire','strong','watch','skip')), paid_candidate BOOLEAN DEFAULT FALSE, tested_as_ad BOOLEAN DEFAULT FALSE, notes TEXT ); CREATE TABLE performance_log ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, created_at TIMESTAMPTZ DEFAULT NOW(), week_start DATE NOT NULL, total_spend NUMERIC(10,2), blended_roas NUMERIC(8,4), active_ad_sets INTEGER, winners INTEGER, losers_killed INTEGER, best_creative TEXT, notes TEXT ); CREATE INDEX idx_paid_label ON paid_creatives(label); CREATE INDEX idx_paid_score ON paid_creatives(score DESC); CREATE INDEX idx_paid_roas ON paid_creatives(roas DESC); CREATE INDEX idx_org_verdict ON organic_content(verdict); CREATE INDEX idx_org_score ON organic_content(score DESC); CREATE INDEX idx_ideas_status ON creative_ideas(status); CREATE OR REPLACE FUNCTION upd_ts() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_ideas_ts BEFORE UPDATE ON creative_ideas FOR EACH ROW EXECUTE FUNCTION upd_ts();
Step 2 — Enable Row Level Security
Locks all tables to authenticated users only. Run after schema creation.
ALTER TABLE creative_ideas ENABLE ROW LEVEL SECURITY; ALTER TABLE paid_creatives ENABLE ROW LEVEL SECURITY; ALTER TABLE organic_content ENABLE ROW LEVEL SECURITY; ALTER TABLE performance_log ENABLE ROW LEVEL SECURITY; CREATE POLICY "auth_all_ideas" ON creative_ideas FOR ALL USING (auth.role()='authenticated') WITH CHECK (auth.role()='authenticated'); CREATE POLICY "auth_all_paid" ON paid_creatives FOR ALL USING (auth.role()='authenticated') WITH CHECK (auth.role()='authenticated'); CREATE POLICY "auth_all_organic" ON organic_content FOR ALL USING (auth.role()='authenticated') WITH CHECK (auth.role()='authenticated'); CREATE POLICY "auth_all_log" ON performance_log FOR ALL USING (auth.role()='authenticated') WITH CHECK (auth.role()='authenticated');
Step 3 — Deploy to Cloudflare Pages
Single HTML file. No build step needed. Direct upload takes 60 seconds.
// OPTION A — Direct Upload (fastest, no CLI needed) // 1. Cloudflare Dashboard → Pages → Create a project → Direct Upload // 2. Name: levelup-creative-os // 3. Drag this HTML file into the upload area // 4. Deploy → done // 5. Custom domain: Pages → Custom domains → levelupfightgame.com/creative-os // OPTION B — GitHub auto-deploy // 1. Push this file as /dist/index.html to a GitHub repo // 2. Cloudflare Pages → Connect to Git → select repo // 3. Build command: (leave empty — static HTML) // 4. Build output: /dist // 5. Every push to main auto-deploys // TO CONNECT SUPABASE (persistent storage): // Add to <head> before closing tag: // <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script> // Then replace localStorage calls with Supabase: // const sb = supabase.createClient('YOUR_URL', 'YOUR_ANON_KEY') // await sb.from('creative_ideas').select('*') // await sb.from('paid_creatives').insert(rows) // await sb.from('organic_content').upsert(rows, {onConflict: 'permalink'}) // Environment variables (set in Cloudflare dashboard → Pages → Settings → Variables): // SUPABASE_URL = https://your-project.supabase.co // SUPABASE_ANON_KEY = your-anon-key
Current State — LocalStorage
All data persists in your browser's localStorage until Supabase is connected. Data survives page refresh and browser close. To migrate: export each table using the Export CSV button and re-import after connecting Supabase.
Add Creative Concept