# Performance Score Calculation ## The Formula ``` Performance Score = Average of five metric scores (0-100) Score = (LCP_score + FCP_score + CLS_score + TBT_score + TTFB_score) / 5 where each metric_score is calculated from thresholds: if metric ≤ good_threshold → metric_score = 100 if metric ≥ poor_threshold → metric_score = 30 if between → metric_score = 100 - ((metric - good) / (poor - good)) × 70 ``` ## Example: rds.ink/endangered = 77 From the database (sitespeed mobile run on 2026-05-13): ``` LCP: NULL → skipped (no data) FCP: 2,116ms → score calculation: good=1,800 poor=3,000 2,116 is between good and poor ratio = (2116 - 1800) / (3000 - 1800) = 316 / 1200 = 0.263 score = 100 - (0.263 × 70) = 100 - 18.4 = 82 points ✓ CLS: 0.0 → score = 100 (well below good threshold of 0.1) ✓ TBT: 1,807ms → score calculation: good=200 poor=600 1,807 >> poor threshold ratio = (1807 - 200) / (600 - 200) = 1607 / 400 = 4.02 Since ratio > 1: score = capped at 30 points ✗ CRITICAL TTFB: 144ms → score = 100 (well below good threshold of 800ms) ✓ Average = (82 + 100 + 30 + 100) / 4 = 78 ≈ 77 (database value) ↑ (rounding) ``` **Bottom line:** TBT (Total Blocking Time) of 1,807ms is **9 times worse** than the 200ms threshold. This single metric alone drops the score from ~90 → 77. ## Thresholds (Hard-Coded) **File:** `/home/help4bis/seo-intel/src/perf/sitespeed.py` lines 53–60 ```python _THRESHOLDS = { # (good_max, poor_min) "lcp": (2500, 4000), # ms "fcp": (1800, 3000), # ms "cls": (0.1, 0.25), # unitless "tbt": (200, 600), # ms "ttfb": (800, 1800), # ms } ``` These thresholds are **based on Google's Lighthouse 10 scoring rubric**. They're not arbitrary — they're what Google uses to score web performance. ## Metric-by-Metric Breakdown ### 1. LCP (Largest Contentful Paint) **What it measures:** How long before the largest visible element (image, heading, paragraph) appears on screen. **Why it matters:** Users need to see that something is happening. **Thresholds:** - **Good:** ≤ 2,500ms (2.5 seconds) - **Poor:** ≥ 4,000ms (4 seconds) **rds.ink status:** Not measured (NULL) **Typical fixes:** - Optimize server response time (TTFB) - Defer non-critical JavaScript - Lazy-load images - Use a CDN for images --- ### 2. FCP (First Contentful Paint) **What it measures:** How long before ANY content (text, image, non-white background) appears. **Why it matters:** The first visual indication that the page is loading. **Thresholds:** - **Good:** ≤ 1,800ms (1.8 seconds) - **Poor:** ≥ 3,000ms (3 seconds) **rds.ink status:** 2,116ms = AMBER (82/100) The page shows content after 2.1 seconds, which is acceptable but slower than ideal. Caused by deferred script execution blocking rendering. **Typical fixes:** - Reduce server response time (TTFB) - Defer non-critical JavaScript - Inline critical CSS - Reduce DOM size --- ### 3. CLS (Cumulative Layout Shift) **What it measures:** How much the page layout jumps around after initial load. **Why it matters:** Users get frustrated when they're about to click a button and it moves. **Thresholds:** - **Good:** ≤ 0.1 (10% of viewport) - **Poor:** ≥ 0.25 (25% of viewport) **rds.ink status:** 0.0 = PERFECT ✓ The page does NOT move after load. Great job. This metric is not the problem. **Typical fixes:** - Set explicit dimensions on images - Avoid inserting content above existing content - Use transform animations instead of position changes --- ### 4. TBT (Total Blocking Time) 🔴 **THE KILLER METRIC** **What it measures:** How long JavaScript blocks the main thread, preventing the browser from responding to user input (clicks, scrolls, etc.). **Why it matters:** A page with 1.8 seconds of TBT feels frozen to the user. **Thresholds:** - **Good:** ≤ 200ms (0.2 seconds) - **Poor:** ≥ 600ms (0.6 seconds) **rds.ink status:** 1,807ms = CRITICAL ❌ The page's JavaScript takes **1.8 seconds** to execute after initial render. During this time: - User clicks "Add to cart" → Nothing happens - User tries to scroll → Page is frozen - User tries to open menu → Unresponsive **Impact on score:** 30/100 points (single worst metric) **Root cause:** Likely WooCommerce plugins, Elementor scripts, and lazy-loaded gallery libraries (Lightbox, PhotoSwipe, Slick, etc.) all executing simultaneously. **Typical fixes (in priority order):** 1. **Defer non-critical JavaScript** (add `defer` attribute to `