Visual Reaction Time

 

It is generally between 200 and 250 ms. This corresponds to the time it takes for a person to react to a visual signal, such as a light turning on.

A load time of 200 ms is similar to or less than the reaction time of a person to a visual stimulus.

This means that a web page displayed in 200 ms seems almost instantaneous to the user, because their brain has not yet begun to perceive a significant delay.


Creating a Chrome extension to measure the TTFB of a website. 

Zip download

https://nicolas-dorriere.fr/assets/files/ttfb-extension.zip

background.js

chrome.runtime.onInstalled.addListener(() => {
  chrome.action.setBadgeBackgroundColor({ color: "#000000" }); // Set default badge background
});

// Listen for tab updates
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.status === 'complete' && 
      tab.url && 
      tab.url.startsWith('http') && 
      !tab.url.includes('chrome-error')) {
    try {
      injectScript(tabId);
    } catch (error) {
      console.log('Failed to inject script:', error);
      // Optionally clear the badge for error pages
      chrome.action.setBadgeText({ text: '', tabId: tabId });
    }
  }
});

// Function to inject the TTFB measurement script into the tab
async function injectScript(tabId) {
  try {
    await chrome.scripting.executeScript({
      target: { tabId: tabId },
      func: getTTFB,
    });
  } catch (error) {
    console.log('Injection error:', error);
    // Clear badge if injection fails
    chrome.action.setBadgeText({ text: '', tabId: tabId });
  }
}

// Function to measure TTFB
function getTTFB() {
  const performanceEntries = performance.getEntriesByType("navigation");
  if (performanceEntries.length > 0) {
    const ttfb = performanceEntries[0].responseStart - performanceEntries[0].requestStart;
    chrome.runtime.sendMessage({ ttfb: Math.round(ttfb) });
  }
}

// Listen for messages with TTFB and update badge
chrome.runtime.onMessage.addListener((message, sender) => {
  if (message.ttfb) {
    const ttfbValue = message.ttfb.toString();

    // Update the badge text with the TTFB value
    chrome.action.setBadgeText({ text: ttfbValue, tabId: sender.tab.id });

    // Change badge background color based on TTFB thresholds
    let badgeColor;
    if (message.ttfb < 50) {
      badgeColor = "#00FF00";  // Green for < 50ms
    } else if (message.ttfb > 200) {
      badgeColor = "#FF0000";  // Red for > 200ms
    } else {
      badgeColor = "#FFFF00";  // Yellow for 50ms - 200ms
    }
    chrome.action.setBadgeBackgroundColor({ color: badgeColor, tabId: sender.tab.id });

    // Set white text on red background
    if (badgeColor === "#FF0000") {
      chrome.action.setBadgeTextColor({ color: "#FFFFFF", tabId: sender.tab.id });
    }
  }
});

manifest.json

{
  "manifest_version": 3,
  "name": "TTFB Display",
  "description": "Displays the Time to First Byte (TTFB) of the current server.",
  "version": "1.0",
  "permissions": ["activeTab", "scripting", "tabs"],
  "host_permissions": ["http://*/*", "https://*/*"],
  "background": {
    "service_worker": "background.js"
  },
  "icons": {
    "16": "icon.png",
    "48": "icon.png",
    "128": "icon.png"
  },
  "action": {
    "default_icon": {
      "16": "icon.png",
      "48": "icon.png",
      "128": "icon.png"
    },
    "default_popup": "popup.html"
  }
}

popup.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <style>
      body {
        width: 300px;
        padding: 10px;
        font-family: Arial, sans-serif;
      }
      p {
        line-height: 1.4;
        margin: 0;
        color: #333;
      }
    </style>
  </head>
  <body>
    <p>
      Le temps de réaction d'un humain oscille entre 200 et 250 ms.<br />
      Il faut donc un TTFB en dessous de 200 ms pour avoir l'impression
      d'instantanéité.
    </p>
  </body>
</html>

Run

 

Download this blog post with all media included in a single HTML file (4,45 Mo)